滥用 Android 媒体管线与图像解析器

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

交付路径: 消息应用 ➜ MediaStore ➜ 特权解析器

现代 OEM 构建通常会运行特权媒体索引器,它们会重新扫描 MediaStore 以支持 “AI” 或共享功能。在未修补到 2025 年 4 月补丁之前的 Samsung 固件上,com.samsung.ipservice 会加载 Quram (/system/lib64/libimagecodec.quram.so) 并自动解析 WhatsApp(或其他应用)放入 MediaStore 的任何文件。实际上,攻击者可以发送一个伪装成 IMG-*.jpg 的 DNG,等待受害者点击 “download”(1-click),即使用户从未打开图库,该特权服务也会解析该载荷。

$ file IMG-2025-02-10.jpeg
TIFF image data ...
$ exiftool IMG-2025-02-10.jpeg | grep "Opcode List"
Opcode List 1 : [opcode 23], [opcode 23], ...

关键要点

  • 交付依赖于系统对媒体的重新解析(而不是 chat client),因此继承了该过程的权限(对图库的完全读/写访问、放置新媒体的能力等)。
  • 任何通过 MediaStore 可访问的 image parser(vision widgets、wallpapers、AI résumé features 等)如果攻击者能够说服目标保存媒体,就会变成可远程触达的目标。

Quram’s DNG Opcode Interpreter Bugs

DNG 文件在不同的解码阶段嵌入三组 opcode 列表。Quram 复制了 Adobe 的 API,但其针对 DeltaPerColumn(opcode ID 11)的 Stage-3 处理器信任攻击者提供的 plane 边界。

DeltaPerColumn 中失效的 plane 边界

  • 攻击者将 plane=5125planes=5123,尽管 Stage-3 图像只暴露 planes 0–2(RGB)。
  • Quram 计算 opcode_last_plane = image_planes + opcode_planes 而不是 plane + count,并且从不检查结果的 plane 范围是否适合图像。
  • 因此循环会向 raw_pixel_buffer[plane_index] 写入一个具有完全可控偏移的 delta(例如 plane 5125 ⇒ 偏移 5125 * 2 bytes/pixel = 0x2800)。每个 opcode 向目标位置添加一个 16-bit float 值 (0x6666),从而产生一个精确的堆 OOB add 原语。

将增量变为任意写

  • 利用 480 个畸形的 DeltaPerColumn 操作,利用者首先损坏 Stage-3 的 QuramDngImage.bottom/right,使未来的 opcode 将巨大的坐标当作在界内处理。
  • 然后将 MapTable opcodes(opcode 7)指向那些伪造的边界。使用全零的 substitution table 或带有 -Inf delta 的 DeltaPerColumn,攻击者可以将任意区域清零,然后再应用额外的 delta 来写入精确值。
  • 因为 opcode 参数存在于 DNG 元数据中,payload 可以在不直接触碰进程内存的情况下编码数十万次写入。

Heap Shaping Under Scudo

Scudo 按大小对分配进行分桶。Quram 恰好将以下对象以相同 0x30 字节的 chunk サイズ 分配,因此它们落在同一区域(堆上以 0x40 字节间距):

  • QuramDngImage 描述符,用于 Stage 1/2/3
  • QuramDngOpcodeTrimBounds 和 厂商 Unknown opcodes(ID ≥14,包括 ID 23)

利用按顺序分配来确定性地放置 chunk:

  1. Stage-1 Unknown(23) opcodes(20,000 条目)喷洒 0x30 chunk,随后这些 chunk 被释放。
  2. Stage-2 释放这些 opcode 并在已释放区域内放置一个新的 QuramDngImage
  3. 240 个 Stage-2 Unknown(23) 条目被释放,Stage-3 立即在这些位置重用并分配其 QuramDngImage 以及一个相同大小的新 raw pixel buffer。
  4. 一个精心构造的 TrimBounds opcode 在列表 3 中首先运行并在释放 Stage-2 状态之前分配了另一个 raw pixel buffer,从而保证了 “raw pixel buffer ➜ QuramDngImage” 的相邻性。
  5. 另外 640 个 TrimBounds 条目被标记为 minVersion=1.4.0.1,调度器因此跳过它们的执行,但它们的 backing objects 保持分配状态并在稍后成为原语目标。

该编排将 Stage-3 的 raw buffer 放在 Stage-3 QuramDngImage 之前,因此基于 plane 的溢出会翻转描述符内的字段,而不是随机崩溃其他状态。

Reusing Vendor “Unknown” Opcodes as Data Blobs

Samsung 在厂商特定的 opcode ID 中保留高位(例如 ID 23),这会指示解释器 allocate 结构但跳过执行。利用者将这些休眠对象当作攻击者可控的堆来滥用:

  • Opcode 列表 1 和 2 的 Unknown(23) 条目作为连续的 scratchpad,用于存储 payload 字节(JOP chain 在相对于 raw buffer 的偏移 0xf000,shell 命令 在 0x10000)。
  • 因为解释器在处理列表 3 时仍将每个对象视为 opcode,占据一个对象的 vtable 即足以开始执行攻击者的数据。

Crafting Bogus MapTable Objects & Bypassing ASLR

MapTable 对象比 TrimBounds 更大,但一旦布局被破坏,解析器会高兴地从越界位置读取额外参数:

  1. 使用线性写入原语部分覆盖一个 TrimBounds 的 vtable 指针,替换为一个伪造的 MapTable substitution table,该表将相邻 TrimBounds vtable 的低 2 字节映射到 MapTable vtable。受支持的 Quram 构建之间低字节差异很小,因此单个 64K 查找表可以覆盖七个固件版本以及每个 4 KB 的 ASLR slide。
  2. 修补 TrimBounds 的其余字段(top/left/width/planes),使该对象在稍后执行时表现得像一个有效的 MapTable
  3. 在被清零的内存上执行这个伪造 opcode。因为 substitution table 指针实际上引用了另一个 opcode 的 vtable,输出字节会变成 leaked 来自 libimagecodec.quram.so 或其 GOT 的低位地址。
  4. 再次应用 MapTable 通过将这两个字节的泄露转换为指向 gadget 的偏移,例如 __ink_jpeg_enc_process_image+64QURAMWINK_Read_IO2+124qpng_check_IHDR+624 和 libc 的 __system_property_get 条目。攻击者实际上在其喷洒的 opcode 区域内重建了完整地址,而无需本地内存泄露 API。

Triggering the JOP ➜ system() Transition

一旦 gadget 指针和 shell 命令在 opcode 喷洒区就绪:

  1. 最后一波 DeltaPerColumn 写入将 Stage-3 QuramDngImage 的偏移 0x22 加上 0x0100,将其 raw buffer 指针移动 0x10000,从而使其现在引用攻击者的命令字符串。
  2. 解释器开始执行尾部的 1040 个 Unknown(23) opcode。第一个被破坏的条目其 vtable 被替换为位于偏移 0xf000 的伪造表,因此 QuramDngOpcode::aboutToApply 将解析出假表中的 qpng_read_data(第 4 项)。
  3. 链接的 gadget 执行:加载 QuramDngImage 指针,加 0x20 指向 raw buffer 指针,解引用它,将结果复制到 x19/x0,然后通过被重写为 system 的 GOT 插槽跳转。因为 raw buffer 指针现在等于攻击者的字符串,最终的 gadget 将在 com.samsung.ipservice 内执行 system(<shell command>)

Notes on Allocator Variants

存在两类 payload:一类针对 jemalloc 调优,另一类针对 scudo。它们在如何对 opcode blocks 排序以实现相邻性上有所不同,但共享相同的逻辑原语(DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP)。Scudo 的禁用隔离(quarantine)使得 0x30 字节 freelist 重用变得确定性,而 jemalloc 则依赖于通过 tile/subIFD 大小来控制 size-class。

References

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks