Abuso delle pipeline multimediali Android e dei parser di immagini
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Modalità di consegna: app di messaggistica ➜ MediaStore ➜ parser privilegiati
Le build OEM moderne eseguono regolarmente indicizzatori multimediali privilegiati che rieseguono la scansione di MediaStore per funzionalità “AI” o di condivisione. Sui firmware Samsung precedenti alla patch di aprile 2025, com.samsung.ipservice carica Quram (/system/lib64/libimagecodec.quram.so) e analizza automaticamente qualsiasi file che WhatsApp (o altre app) inserisce in MediaStore. In pratica un attaccante può inviare un DNG camuffato da IMG-*.jpg, attendere che la vittima tocchi “download” (1 clic), e il servizio privilegiato analizzerà il payload anche se l’utente non apre mai la galleria.
$ 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], ...
Punti chiave
- La delivery si basa sulla re-parsing dei media di sistema (non sul client di chat) e quindi eredita i permessi di quel processo (accesso completo in lettura/scrittura alla gallery, capacità di inserire nuovi media, ecc.).
- Qualsiasi image parser raggiungibile tramite
MediaStore(vision widgets, wallpapers, AI résumé features, ecc.) diventa raggiungibile da remoto se l’attaccante riesce a convincere la vittima a salvare media.
0-click DD+/EAC-3 decoding path (Google Messages ➜ mediacodec sandbox)
Modern messaging stacks decodificano automaticamente anche l’audio per trascrizioni/ricerche. Su Pixel 9, Google Messages passa l’audio RCS/SMS in arrivo al Dolby Unified Decoder (UDC) dentro /vendor/lib64/libcodec2_soft_ddpdec.so prima che l’utente apra il messaggio, estendendo la superficie 0-click ai media codecs.
Vincoli chiave del parsing
- Ogni DD+ syncframe ha fino a 6 block; ogni block può copiare fino a
0x1FFbyte di skip data controllati dall’attaccante in un skip buffer (≈0x1FF * 6byte per frame). - Lo skip buffer viene scansionato per EMDF:
syncword (0xX8)+emdf_container_length(16b) + campi a lunghezza variabile.emdf_payload_sizeè parsato con un loopvariable_bits(8)senza bound. - I byte del payload EMDF sono allocati dentro un bump allocator per-frame personalizzato chiamato “evo heap” e poi copiati byte-per-byte da un bit-reader limitato da
emdf_container_length.
Integer-overflow → heap-overflow primitive (CVE-2025-54957)
ddp_udc_int_evo_mallocallineaalloc_size+extraa 8 byte tramitetotal_size += (8 - total_size) % total_sizesenza rilevazione di wrap. Valori vicini a0xFFFFFFFFFFFFFFF9..FFsi riducono a untotal_sizemolto piccolo su AArch64.- Il loop di copia usa comunque la
payload_lengthlogica daemdf_payload_size, quindi i byte dell’attaccante sovrascrivono dati dell’evo-heap oltre il chunk sottodimensionato. - La lunghezza dell’overflow è esattamente limitata da
emdf_container_lengthscelto dall’attaccante; i byte di overflow sono dati EMDF controllati dall’attaccante. Lo slab allocator viene resettato ad ogni syncframe, dando adiacenza prevedibile.
Primitive di lettura secondaria
Se emdf_container_length > skipl, il parsing EMDF legge oltre i byte inizializzati dello skip (OOB read). Da solo rivela zeri/media noti, ma dopo aver corrotto metadata adiacenti dell’heap può leggere indietro la regione corrotta per validare l’exploit.
Ricetta di exploit
- Costruire un EMDF con
emdf_payload_sizeenorme (tramitevariable_bits(8)) in modo che il padding dell’allocatore vada in wrap su un chunk piccolo. - Impostare
emdf_container_lengthalla lunghezza d’overflow desiderata (≤ budget totale di skip); inserire i byte di overflow nel payload EMDF. - Modellare l’evo heap per-frame così che la piccola allocazione sia prima delle strutture target dentro il buffer statico del decoder (≈693 KB) o il buffer dinamico (≈86 KB) allocato una volta per istanza del decoder.
- Opzionalmente scegliere
emdf_container_length > skiplper leggere indietro i dati sovrascritti dallo skip buffer dopo la corruzione.
Quram’s DNG Opcode Interpreter Bugs
I file DNG incorporano tre liste di opcode applicate in stadi diversi di decodifica. Quram copia l’API di Adobe, ma il suo handler di Stage-3 per DeltaPerColumn (opcode ID 11) si fida dei bounds del plane forniti dall’attaccante.
Bounds dei plane errati in DeltaPerColumn
- Gli attaccanti impostano
plane=5125eplanes=5123anche se le immagini Stage-3 espongono solo i plane 0–2 (RGB). - Quram calcola
opcode_last_plane = image_planes + opcode_planesinvece diplane + count, e non verifica mai se il range risultante dei plane rientra nell’immagine. - Il loop quindi scrive un delta in
raw_pixel_buffer[plane_index]con un offset completamente controllato (es., plane 5125 ⇒ offset5125 * 2 bytes/pixel = 0x2800). Ogni opcode aggiunge un valore float a 16-bit (0x6666) alla posizione target, fornendo una primitiva di heap OOB add precisa.
Trasformare incrementi in scritture arbitrarie
- L’exploit prima corrompe
QuramDngImage.bottom/rightdi Stage-3 usando 480DeltaPerColumnmalformati così che opcode futuri trattino coordinate enormi come in-bounds. - Gli opcode
MapTable(opcode 7) vengono poi diretti a quei bounds finti. Usando una tabella di sostituzione tutta zeri o unDeltaPerColumncon delta-Inf, l’attaccante azzera qualunque regione, poi applica delta addizionali per scrivere valori esatti. - Poiché i parametri degli opcode risiedono nei metadata DNG, il payload può codificare centinaia di migliaia di scritture senza toccare direttamente la memoria di processo.
Heap Shaping Under Scudo
Scudo raggruppa le allocazioni per size. Quram per caso alloca i seguenti oggetti con chunk di dimensione identica 0x30, quindi finiscono nella stessa regione (spaziatura di 0x40 byte sull’heap):
- descriptor
QuramDngImageper Stage 1/2/3 QuramDngOpcodeTrimBoundse opcode vendorUnknown(ID ≥14, incluso ID 23)
L’exploit ordina le allocazioni per posizionare i chunk in modo deterministico:
- Opcode Stage-1
Unknown(23)(20.000 entries) spruzzano chunk da 0x30 che poi vengono freed. - Stage-2 freea quegli opcode e piazza un nuovo
QuramDngImagenella regione liberata. - 240 entry Stage-2
Unknown(23)vengono freeate, e Stage-3 alloca immediatamente il suoQuramDngImagepiù un nuovo raw pixel buffer della stessa dimensione, riutilizzando quegli slot. - Un
TrimBoundscostruito corre per primo nella lista 3 e alloca un altro raw pixel buffer prima di freeare lo stato di Stage-2, garantendo l’adiacenza “raw pixel buffer ➜ QuramDngImage”. - 640
TrimBoundsaggiuntivi sono marcatiminVersion=1.4.0.1così il dispatcher li salta, ma i loro oggetti sottostanti restano allocati e più tardi diventano bersagli primitivi.
Questa coreografia mette il raw buffer di Stage-3 immediatamente prima del QuramDngImage di Stage-3, quindi l’overflow basato sui plane ribalta campi dentro il descriptor anziché causare crash di stato casuale.
Reusing Vendor “Unknown” Opcodes as Data Blobs
Samsung lascia il bit alto settato negli ID opcode vendor-specific (es., ID 23), il che istruisce l’interprete ad allocare la struttura ma saltarne l’esecuzione. L’exploit abusa di quegli oggetti dormienti come heap controllati dall’attaccante:
- Le entries
Unknown(23)delle liste opcode 1 e 2 servono come scratchpad contiguo per memorizzare byte del payload (JOP chain a offset 0xf000 e un comando shell a 0x10000 relativo al raw buffer). - Poiché l’interprete tratta ancora ogni oggetto come un opcode quando la lista 3 viene processata, conquistare la vtable di un oggetto è sufficiente per iniziare l’esecuzione dei dati dell’attaccante.
Crafting Bogus MapTable Objects & Bypassing ASLR
Gli oggetti MapTable sono più grandi dei TrimBounds, ma una volta che la corruzione del layout si verifica, il parser legge volentieri parametri extra out-of-bounds:
- Usare la primitiva di scrittura lineare per sovrascrivere parzialmente un puntatore vtable di
TrimBoundscon una tabella di sostituzioneMapTablecostruita che mappa i due byte bassi da una vtableTrimBoundsvicina alla vtableMapTable. Solo i byte bassi differiscono tra le build Quram supportate, quindi una singola lookup table da 64K può gestire sette versioni firmware e ogni slide ASLR da 4 KB. - Patchare il resto dei campi di
TrimBounds(top/left/width/planes) così che l’oggetto si comporti come unMapTablevalido quando eseguito più tardi. - Eseguire l’opcode fake su memoria azzerata. Poiché il puntatore alla substitution table in realtà punta alla vtable di un altro opcode, i byte di output diventano leaked indirizzi low-order da
libimagecodec.quram.soo dal suo GOT. - Applicare ulteriori passaggi
MapTableper convertire quei leak di due byte in offset verso gadget come__ink_jpeg_enc_process_image+64,QURAMWINK_Read_IO2+124,qpng_check_IHDR+624, e l’entry di libc__system_property_get. Gli attaccanti ricostruiscono effettivamente indirizzi completi dentro la loro regione di spray di opcode senza API native di disclosure di memoria.
Triggering the JOP ➜ system() Transition
Una volta che i puntatori ai gadget e il comando shell sono piazzati nello spray di opcode:
- Un’ultima ondata di scritture
DeltaPerColumnaggiunge0x0100all’offset 0x22 delQuramDngImagedi Stage-3, spostando il suo raw buffer pointer di 0x10000 così che ora punti alla stringa del comando dell’attaccante. - L’interprete comincia ad eseguire la coda di 1040 opcode
Unknown(23). La prima entry corrotta ha la sua vtable sostituita con la tabella falsificata a offset 0xf000, cosìQuramDngOpcode::aboutToApplyrisolveqpng_read_data(la 4a voce) dalla fake table. - I gadget concatenati eseguono: caricano il puntatore
QuramDngImage, aggiungono 0x20 per puntare al raw buffer pointer, lo dereferenziano, copiano il risultato inx19/x0, poi saltano attraverso slot GOT riscritti asystem. Poiché il raw buffer pointer ora è la stringa dell’attaccante, il gadget finale eseguesystem(<shell command>)dentrocom.samsung.ipservice.
Note sulle varianti dell’allocator
Esistono due famiglie di payload: una tarata per jemalloc, un’altra per scudo. Differiscono nell’ordine dei blocchi opcode per ottenere l’adiacenza ma condividono le stesse primitive logiche (bug DeltaPerColumn ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). La quarantine disabilitata di Scudo rende il reuse della freelist da 0x30 byte deterministico, mentre jemalloc si affida al controllo della size-class tramite sizing di tile/subIFD.
References
- Project Zero – A look at an Android ITW DNG exploit
- Project Zero – Pixel 0-click: CVE-2025-54957 in Dolby UDC
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


