Abuso de pipelines de medios y analizadores de imágenes en Android

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Delivery: Messaging Apps ➜ MediaStore ➜ Privileged Parsers

Las builds OEM modernas suelen ejecutar indexadores de medios privilegiados que vuelven a escanear MediaStore para funciones de “AI” o de compartición. En el firmware de Samsung anterior al parche de abril de 2025, com.samsung.ipservice carga Quram (/system/lib64/libimagecodec.quram.so) y analiza automáticamente cualquier archivo que WhatsApp (u otras apps) deje en MediaStore. En la práctica, un atacante puede enviar un DNG disfrazado como IMG-*.jpg, esperar a que la víctima toque “descargar” (1-click), y el servicio privilegiado analizará la carga útil incluso si el usuario nunca abre la galería.

$ 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], ...

Conclusiones clave

  • La entrega depende del re-procesado del sistema de medios (no del cliente de chat) y por tanto hereda los permisos de ese proceso (acceso completo de lectura/escritura a la galería, capacidad de agregar nuevos medios, etc.).
  • Cualquier parser de imágenes accesible a través de MediaStore (vision widgets, wallpapers, funciones de resumen de IA, etc.) se vuelve accesible remotamente si el atacante convence a la víctima de guardar medios.

Quram’s DNG Opcode Interpreter Bugs

Los archivos DNG incrustan tres listas de opcodes aplicadas en diferentes etapas de decodificación. Quram copia la API de Adobe, pero su manejador de Stage-3 para DeltaPerColumn (opcode ID 11) confía en los límites de plano suministrados por el atacante.

Límites de plano inválidos en DeltaPerColumn

  • Los atacantes establecen plane=5125 y planes=5123 aunque las imágenes Stage-3 solo exponen planos 0–2 (RGB).
  • Quram calcula opcode_last_plane = image_planes + opcode_planes en vez de plane + count, y nunca comprueba si el rango de planos resultante encaja dentro de la imagen.
  • Por tanto el bucle escribe un delta en raw_pixel_buffer[plane_index] con un desplazamiento completamente controlado (p. ej., plane 5125 ⇒ offset 5125 * 2 bytes/pixel = 0x2800). Cada opcode suma un valor float de 16 bits (0x6666) a la ubicación objetivo, produciendo una primitiva precisa de suma OOB en el heap.

Convertir incrementos en escrituras arbitrarias

  • El exploit primero corrompe QuramDngImage.bottom/right de Stage-3 usando 480 operaciones DeltaPerColumn malformadas para que opcodes posteriores traten coordenadas enormes como válidas.
  • MapTable opcodes (opcode 7) se dirigen luego a esos límites falsos. Usando una tabla de sustitución de ceros o un DeltaPerColumn con deltas -Inf, el atacante pone a cero cualquier región y después aplica deltas adicionales para escribir valores exactos.
  • Porque los parámetros de los opcodes viven dentro de los metadatos DNG, el payload puede codificar cientos de miles de escrituras sin tocar directamente la memoria del proceso.

Heap Shaping Under Scudo

Scudo agrupa las allocations por tamaño. Quram asigna los siguientes objetos con tamaños de chunk idénticos de 0x30 bytes, por lo que caen en la misma región (espaciado de 0x40 bytes en el heap):

  • QuramDngImage descriptors for Stage 1/2/3
  • QuramDngOpcodeTrimBounds and vendor Unknown opcodes (ID ≥14, including ID 23)

El exploit encadena las asignaciones para colocar los chunks de forma determinista:

  1. Los Unknown(23) opcodes de Stage-1 (20,000 entradas) rocían chunks de 0x30 que luego se liberan.
  2. Stage-2 libera esos opcodes y coloca un nuevo QuramDngImage dentro de la región liberada.
  3. Se liberan 240 entradas Unknown(23) de Stage-2, y Stage-3 asigna inmediatamente su QuramDngImage más un nuevo raw pixel buffer del mismo tamaño, reutilizando esos espacios.
  4. Un TrimBounds confeccionado se ejecuta primero en la lista 3 y asigna otro raw pixel buffer antes de liberar el estado de Stage-2, garantizando la adyacencia “raw pixel buffer ➜ QuramDngImage”.
  5. 640 entradas adicionales TrimBounds están marcadas minVersion=1.4.0.1 para que el dispatcher las salte, pero sus objetos subyacentes permanecen asignados y más tarde se convierten en objetivos primitivos.

Esta coreografía coloca el raw buffer de Stage-3 inmediatamente antes del QuramDngImage de Stage-3, de modo que el overflow basado en planos modifica campos dentro del descriptor en lugar de provocar fallos en estados aleatorios.

Reusing Vendor “Unknown” Opcodes as Data Blobs

Samsung mantiene el bit alto activado en los IDs de opcode específicos del vendor (p. ej., ID 23), lo que instruye al intérprete para allocate la estructura pero omitir la ejecución. El exploit abusa de esos objetos dormidos como heaps controlados por el atacante:

  • Las entradas Unknown(23) de las listas de opcode 1 y 2 sirven como pads contiguos para almacenar bytes del payload (JOP chain en offset 0xf000 y un comando shell en 0x10000 relativo al raw buffer).
  • Porque el intérprete todavía trata cada objeto como un opcode cuando se procesa la lista 3, tomar el control del vtable de un objeto después es suficiente para empezar a ejecutar los datos del atacante.

Crafting Bogus MapTable Objects & Bypassing ASLR

Los objetos MapTable son más grandes que TrimBounds, pero una vez que llega la corrupción de layout, el parser lee con gusto parámetros extra fuera de límites:

  1. Usar la primitiva de escritura lineal para sobrescribir parcialmente un puntero vtable de TrimBounds con una tabla de sustitución MapTable creada que mapea los 2 bytes bajos de un vtable TrimBounds vecino al vtable de MapTable. Solo los bytes bajos difieren entre builds de Quram soportados, así que una sola tabla de búsqueda de 64K puede manejar siete versiones de firmware y cada desplazamiento de ASLR de 4 KB.
  2. Parchear el resto de campos de TrimBounds (top/left/width/planes) para que el objeto se comporte como un MapTable válido cuando se ejecute más tarde.
  3. Ejecutar el opcode falso sobre memoria puesta a cero. Como el puntero a la tabla de sustitución en realidad referencia el vtable de otro opcode, los bytes de salida se convierten en direcciones de orden bajo leaked de libimagecodec.quram.so o su GOT.
  4. Aplicar pases adicionales de MapTable para convertir esos leaks de dos bytes en offsets hacia gadgets como __ink_jpeg_enc_process_image+64, QURAMWINK_Read_IO2+124, qpng_check_IHDR+624 y la entrada __system_property_get de libc. Los atacantes efectivamente reconstruyen direcciones completas dentro de su región de opcodes rociada sin APIs nativas de divulgación de memoria.

Triggering the JOP ➜ system() Transition

Una última oleada de escrituras DeltaPerColumn suma 0x0100 al offset 0x22 del QuramDngImage de Stage-3, desplazando su puntero de raw buffer en 0x10000 para que ahora haga referencia a la cadena de comando del atacante.

  1. El intérprete comienza a ejecutar la cola de 1040 opcodes Unknown(23). La primera entrada corrompida tiene su vtable reemplazado con la tabla forjada en offset 0xf000, así que QuramDngOpcode::aboutToApply resuelve qpng_read_data (la 4ª entrada) desde la tabla falsa.
  2. Los gadgets encadenados realizan: cargar el puntero QuramDngImage, sumar 0x20 para apuntar al puntero del raw buffer, desreferenciarlo, copiar el resultado en x19/x0, y luego saltar a través de slots GOT reescritos a system. Como el puntero del raw buffer ahora equivale a la cadena del atacante, el gadget final ejecuta system(<shell command>) dentro de com.samsung.ipservice.

Notes on Allocator Variants

Existen dos familias de payloads: una ajustada para jemalloc y otra para Scudo. Difieren en cómo se ordenan los bloques de opcode para lograr adyacencia, pero comparten las mismas primitivas lógicas (DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). La cuarentena deshabilitada de Scudo hace que la reutilización de freelist de 0x30 bytes sea determinista, mientras que jemalloc se basa en el control de clase de tamaño mediante el dimensionado de tile/subIFD.

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks