Wykorzystywanie potoków multimedialnych Androida i parserów obrazów
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Dostarczanie: aplikacje komunikacyjne ➜ MediaStore ➜ uprzywilejowane parsery
Nowoczesne buildy OEM regularnie uruchamiają uprzywilejowane indeksatory mediów, które ponownie skanują MediaStore pod kątem funkcji “AI” lub udostępniania. Na firmware Samsung przed łatką z kwietnia 2025 com.samsung.ipservice ładuje Quram (/system/lib64/libimagecodec.quram.so) i automatycznie parsuje każdy plik, który WhatsApp (lub inne aplikacje) umieszcza w MediaStore. W praktyce atakujący może wysłać DNG podszyty pod IMG-*.jpg, poczekać aż ofiara stuknie “download” (1-click), a uprzywilejowana usługa sparsuje payload nawet jeśli użytkownik nigdy nie otworzy galerii.
$ 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], ...
Kluczowe wnioski
- Dostarczanie opiera się na systemowym ponownym parsowaniu mediów (nie kliencie czatu) i w związku z tym dziedziczy uprawnienia tego procesu (pełny dostęp do odczytu/zapisu galerii, możliwość umieszczania nowych mediów itp.).
- Każdy parser obrazów dostępny przez
MediaStore(vision widgets, wallpapers, AI résumé features itp.) staje się zdalnie osiągalny, jeśli atakujący przekona cel do zapisania mediów.
0-click DD+/EAC-3 decoding path (Google Messages ➜ mediacodec sandbox)
Nowoczesne stosy messagingowe także automatycznie dekodują audio do transkrypcji/wyszukiwania. Na Pixel 9 Google Messages przekaże przychodzące audio RCS/SMS do Dolby Unified Decoder (UDC) w /vendor/lib64/libcodec2_soft_ddpdec.so zanim użytkownik otworzy wiadomość, rozszerzając 0-click powierzchnię do kodeków multimedialnych.
Kluczowe ograniczenia parsowania
- Każdy DD+ syncframe ma do 6 bloków; każdy blok może skopiować do
0x1FFbajtów kontrolowanych przez atakującego skip data do skip buffer (≈0x1FF * 6bajtów na ramkę). - Skip buffer jest skanowany w poszukiwaniu EMDF:
syncword (0xX8)+emdf_container_length(16b) + pola o zmiennej długości.emdf_payload_sizejest parsowane pętląvariable_bits(8)bez ograniczeń. - Bajty ładunku EMDF są alokowane wewnątrz niestandardowego per-frame “evo heap” bump allocator i następnie kopiowane bajt-po-bajcie z bit-readera ograniczonego przez
emdf_container_length.
Integer-overflow → heap-overflow primitive (CVE-2025-54957)
ddp_udc_int_evo_mallocwyrównujealloc_size+extrado 8 bajtów przeztotal_size += (8 - total_size) % total_sizebez wykrywania przepełnienia. Wartości bliskie0xFFFFFFFFFFFFFFF9..FFkurczą się do bardzo małegototal_sizena AArch64.- Pętla kopiująca nadal używa logicznej
payload_lengthzemdf_payload_size, więc bajty atakującego nadpisują dane evo-heap poza zbyt małym chunkiem. - Długość overflow jest precyzyjnie ograniczona przez kontrolowany przez atakującego
emdf_container_length; bajty overflow są kontrolowanymi danymi ładunku EMDF. Slab allocator jest resetowany dla każdej syncframe, co daje przewidywalną sąsiedztwo.
Drugorzędny prymityw odczytu
Jeśli emdf_container_length > skipl, parsowanie EMDF odczytuje poza zainicjalizowane bajty skip (OOB read). Samo w sobie leaks zera/znane media, ale po uszkodzeniu sąsiednich metadanych sterty może odczytać z powrotem uszkodzony region, by zweryfikować exploit.
Przepis eksploatacji
- Stwórz EMDF z ogromnym
emdf_payload_size(przezvariable_bits(8)), tak aby padding alokatora zawinął się do małego chanka. - Ustaw
emdf_container_lengthna pożądaną długość overflow (≤ całkowity budżet skip data); umieść bajty overflow w ładunku EMDF. - Ukształtuj per-frame evo heap tak, aby mała alokacja znalazła się przed celowymi strukturami wewnątrz statycznego bufora dekodera (≈693 KB) lub dynamicznego bufora (≈86 KB) alokowanego raz na instancję dekodera.
- Opcjonalnie wybierz
emdf_container_length > skipl, aby odczytać z powrotem nadpisane dane ze skip buffer po korupcji.
Quram’s DNG Opcode Interpreter Bugs
Pliki DNG osadzają trzy listy opcode’ów stosowanych na różnych etapach dekodowania. Quram kopiuje API Adobe, ale jego handler Stage-3 dla DeltaPerColumn (opcode ID 11) ufa dostarczonym przez atakującego ograniczeniom płaszczyzny.
Failing plane bounds in DeltaPerColumn
- Atakujący ustawiają
plane=5125iplanes=5123, mimo że obrazy Stage-3 eksponują tylko płaszczyzny 0–2 (RGB). - Quram oblicza
opcode_last_plane = image_planes + opcode_planeszamiastplane + count, i nigdy nie sprawdza, czy wynikowy zakres płaszczyzn mieści się w obrazie. - Pętla więc zapisuje delta do
raw_pixel_buffer[plane_index]z całkowicie kontrolowanym offsetem (np. plane 5125 ⇒ offset5125 * 2 bytes/pixel = 0x2800). Każdy opcode dodaje wartość 16-bit float (0x6666) do wskazanego miejsca, dając precyzyjny heap OOB add prymityw.
Turning increments into arbitrary writes
- Exploit najpierw uszkadza Stage-3
QuramDngImage.bottom/rightużywając 480 sfałszowanych operacjiDeltaPerColumn, tak by przyszłe opcode’y traktowały ogromne współrzędne jako mieszczące się w granicach. - Opcode’y
MapTable(opcode 7) są następnie skierowane na te fałszywe granice. Używając tabeli substytucji wypełnionej zerami lubDeltaPerColumnz delta =-Inf, atakujący zeruje dowolny region, a potem stosuje dodatkowe delty, by zapisać dokładne wartości. - Ponieważ parametry opcode’ów żyją w metadanych DNG, ładunek może zakodować setki tysięcy zapisów bez bezpośredniego dotykania pamięci procesu.
Heap Shaping Under Scudo
Scudo grupuje alokacje według rozmiaru. Quram ma alokacje następujących obiektów o identycznym rozmiarze chunk 0x30, więc trafiają w to samo miejsce (odstęp 0x40 na stercie):
QuramDngImagedeskryptory dla Stage 1/2/3QuramDngOpcodeTrimBoundsoraz vendorUnknownopcodes (ID ≥14, w tym ID 23)
Exploit sekwencjonuje alokacje, aby deterministycznie umieścić chunki:
- Stage-1
Unknown(23)opcode’y (20,000 wpisów) spraye 0x30 chunki, które potem są zwalniane. - Stage-2 zwalnia te opcode’y i umieszcza nowy
QuramDngImagew zwolnionym regionie. - 240 Stage-2
Unknown(23)wpisów jest zwalnianych, a Stage-3 natychmiast alokuje swójQuramDngImageplus nowy raw pixel buffer tej samej wielkości, ponownie używając tych miejsc. - Sfałszowany opcode
TrimBoundsuruchamia się pierwszy w liście 3 i alokuje kolejny raw pixel buffer zanim Stage-2 stan zostanie zwolniony, gwarantując sąsiedztwo “raw pixel buffer ➜ QuramDngImage”. - Kolejne 640
TrimBoundswpisów oznaczono jakominVersion=1.4.0.1, więc dispatcher je pomija, lecz ich obiekty pozostają alokowane i później stają się celami prymitywów.
Ta choreografia umieszcza Stage-3 raw buffer natychmiast przed Stage-3 QuramDngImage, tak że overflow oparty na płaszczyznach odwraca pola wewnątrz deskryptora, zamiast losowo przerywać stan.
Reusing Vendor “Unknown” Opcodes as Data Blobs
Samsung zostawia ustawiony high bit w vendor-specific ID opcode’ów (np. ID 23), co instruuje interpreter, by alokował strukturę, ale pominął jej wykonanie. Exploit nadużywa tych uśpionych obiektów jako stert kontrolowanych przez atakującego:
- Lista opcode’ów 1 i 2
Unknown(23)służy jako sąsiadujące scratchpady do przechowywania bajtów payloadu (JOP chain na offsetcie 0xf000 i polecenie shell na 0x10000 względem raw buffer). - Ponieważ interpreter nadal traktuje każdy obiekt jako opcode gdy lista 3 jest przetwarzana, przejęcie vtable jednego obiektu później wystarcza, żeby zacząć wykonywać dane atakującego.
Crafting Bogus MapTable Objects & Bypassing ASLR
Obiekty MapTable są większe niż TrimBounds, ale gdy korupcja layoutu wystąpi, parser chętnie czyta dodatkowe parametry poza granicami:
- Użyj liniowego prymitywu zapisu, by częściowo nadpisać wskaźnik vtable
TrimBoundsspreparowaną tabelą substytucjiMapTable, która mapuje niższe 2 bajty z sąsiedniego vtableTrimBoundsdo vtableMapTable. Tylko niskie bajty różnią się między wspieranymi buildami Quram, więc pojedyncza 64K tabela lookup obsłuży siedem wersji firmware i każdy 4 KB ASLR slide. - Załatuj resztę pól
TrimBounds(top/left/width/planes), tak by obiekt zachowywał się jak ważnyMapTableprzy późniejszym wykonaniu. - Wykonaj fałszywy opcode nad wyzerowaną pamięcią. Ponieważ wskaźnik tabeli substytucji faktycznie referuje do vtable innego opcode’a, wyjściowe bajty stają się leaked low-order addresses z
libimagecodec.quram.solub z jego GOT. - Zastosuj dodatkowe przebiegi
MapTable, by przekształcić te dwubajtowe leaks w przesunięcia w kierunku gadgetów takich jak__ink_jpeg_enc_process_image+64,QURAMWINK_Read_IO2+124,qpng_check_IHDR+624oraz libc__system_property_get. Atakujący efektywnie odbudowują pełne adresy wewnątrz swojej spraye’owanej regionu opcode’ów bez natywnych API do ujawniania pamięci.
Triggering the JOP ➜ system() Transition
Gdy wskaźniki gadgetów i polecenie shell są ustawione wewnątrz opcode spray:
- Ostateczna fala zapisów
DeltaPerColumndodaje0x0100do offsetu 0x22 w Stage-3QuramDngImage, przesuwając jego wskaźnik raw buffer o 0x10000, tak że teraz wskazuje na string polecenia atakującego. - Interpreter zaczyna wykonywać tail 1040
Unknown(23)opcode’ów. Pierwszy uszkodzony wpis ma vtable zastąpiony spreparowaną tabelą na offsetcie 0xf000, więcQuramDngOpcode::aboutToApplyrozwiązujeqpng_read_data(4. wpis) z fałszywej tabeli. - Połączone gadgety wykonują: ładują wskaźnik
QuramDngImage, dodają 0x20 by pokazać na wskaźnik raw buffer, dereferencują go, kopiują wynik dox19/x0, a następnie skaczą przez GOT sloty przepisane nasystem. Ponieważ wskaźnik raw buffer teraz równa się stringowi atakującego, finalny gadget wykonujesystem(<polecenie shell>)wewnątrzcom.samsung.ipservice.
Notes on Allocator Variants
Istnieją dwie rodziny payloadów: jedna dostrojona pod jemalloc, druga pod scudo. Różnią się kolejnością bloków opcode, by osiągnąć sąsiedztwo, ale dzielą te same logiczne prymtywy (DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). Wyłączona kwarantanna Scudo sprawia, że reuse freelistu 0x30 bajtów jest deterministyczny, podczas gdy jemalloc polega na kontroli klas rozmiarów przez tile/subIFD sizing.
References
- Project Zero – A look at an Android ITW DNG exploit
- Project Zero – Pixel 0-click: CVE-2025-54957 in Dolby UDC
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.


