Nadużywanie Android Media Pipelines & Image Parsers

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

Dostarczanie: aplikacje wiadomości ➜ MediaStore ➜ uprzywilejowane parsery

Nowoczesne buildy OEM regularnie uruchamiają uprzywilejowane indeksatory multimediów, które ponownie skanują MediaStore w poszukiwaniu funkcji związanych z “AI” lub udostępnianiem. Na firmware Samsunga przed patchem 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) wrzucą do MediaStore. W praktyce atakujący może wysłać DNG podszywający się 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], ...

Najważniejsze wnioski

  • Dostarczenie opiera się na ponownym parsowaniu mediów przez system (nie na kliencie czatu) i w związku z tym dziedziczy uprawnienia tego procesu (pełny dostęp do odczytu/zapisu galerii, możliwość dodawania 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.

Quram’s DNG Opcode Interpreter Bugs

Pliki DNG osadzają trzy listy opcode’ów stosowane na różnych etapach dekodowania. Quram kopiuje API Adobe, ale jego handler Stage-3 dla DeltaPerColumn (opcode ID 11) ufa parametrów plane dostarczonych przez atakującego.

Nieprawidłowe granice plane w DeltaPerColumn

  • Atakujący ustawiają plane=5125 i planes=5123, mimo że obrazy Stage-3 udostępniają tylko planes 0–2 (RGB).
  • Quram oblicza opcode_last_plane = image_planes + opcode_planes zamiast plane + count, i nigdy nie sprawdza, czy wynikowy zakres plane mieści się w obrazie.
  • Pętla zatem zapisuje delta do raw_pixel_buffer[plane_index] z w pełni kontrolowanym offsetem (np. plane 5125 ⇒ offset 5125 * 2 bytes/pixel = 0x2800). Każdy opcode dodaje wartość 16-bitowego floatu (0x6666) do docelowej lokalizacji, dając precyzyjną heap OOB add primitive.

Zmiana inkrementów w dowolne zapisy

  • Eksploit najpierw uszkadza Stage-3 QuramDngImage.bottom/right używając 480 sfałszowanych operacji DeltaPerColumn, tak aby przyszłe opcode’y traktowały ogromne współrzędne jako mieszczące się w obrębie obrazu.
  • MapTable opcode’y (opcode 7) są następnie skierowane na te fałszywe granice. Używając tabeli podstawień wypełnionej zerami lub DeltaPerColumn z -Inf deltas, atakujący zeruje dowolny region, a następnie stosuje dodatkowe delty, żeby zapisać dokładne wartości.
  • Ponieważ parametry opcode’ów znajdują się w metadanych DNG, payload może zakodować setki tysięcy zapisów bez bezpośredniego dotykania pamięci procesu.

Heap Shaping Under Scudo

Scudo dzieli alokacje na kubełki według rozmiaru. Quram przydziela następujące obiekty o identycznym rozmiarze kawałków 0x30 bajtów, więc trafiają one w ten sam region (0x40-bajtowe odstępy na stercie):

  • deskryptory QuramDngImage dla Stage 1/2/3
  • QuramDngOpcodeTrimBounds oraz vendor Unknown opcode’y (ID ≥14, w tym ID 23)

Eksploit sekwencyjnie wykonuje alokacje, aby deterministycznie umieścić kawałki:

  1. Opcode’y Stage-1 Unknown(23) (20,000 wpisów) rozsiewają 0x30 kawałków, które później są zwalniane.
  2. Stage-2 zwalnia te opcode’y i umieszcza nowy QuramDngImage w zwolnionym regionie.
  3. 240 wpisów Stage-2 Unknown(23) jest zwolnionych, a Stage-3 natychmiast alokuje swój QuramDngImage oraz nowy raw pixel buffer o tym samym rozmiarze, ponownie używając tych miejsc.
  4. Sfałszowany TrimBounds opcode wykonywany jako pierwszy na liście 3 alokuje kolejny raw pixel buffer przed zwolnieniem stanu Stage-2, gwarantując przyleganie “raw pixel buffer ➜ QuramDngImage”.
  5. Kolejne 640 wpisów TrimBounds oznaczono jako minVersion=1.4.0.1, więc dispatcher je pomija, ale ich obiekty wspierające pozostają zaalokowane i później stają się celami dla prymitywów.

Ta choreografia umieszcza Stage-3 raw buffer bezpośrednio przed Stage-3 QuramDngImage, więc overflow oparty na plane modyfikuje pola wewnątrz deskryptora zamiast powodować przypadkowe uszkodzenie stanu.

Reusing Vendor “Unknown” Opcodes as Data Blobs

Samsung pozostawia ustawiony najwyższy bit w vendor-specific opcode IDs (np. ID 23), co instruuje interpreter, aby alokować strukturę, ale pominąć wykonanie. Eksploit wykorzystuje te uśpione obiekty jako sterty kontrolowane przez atakującego:

  • Wpisy Unknown(23) na listach opcode 1 i 2 służą jako ciągłe 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 podczas przetwarzania listy 3, przejęcie vtable jednego obiektu później wystarcza, aby zacząć wykonywać dane atakującego.

Crafting Bogus MapTable Objects & Bypassing ASLR

MapTable obiekty są większe niż TrimBounds, ale kiedy korupcja układu nastąpi, parser chętnie odczytuje dodatkowe parametry poza granicami:

  1. Użyj linear write primitive, aby częściowo nadpisać wskaźnik vtable TrimBounds sfałszowaną tabelą substytucji MapTable, która mapuje dolne 2 bajty z sąsiedniego vtable TrimBounds na vtable MapTable. Tylko niskie bajty różnią się między wspieranymi buildami Quram, więc jedna 64K tabela lookup może obsłużyć siedem wersji firmware i każdy 4 KB ASLR slide.
  2. Załatuj resztę pól TrimBounds (top/left/width/planes), aby obiekt zachowywał się jak poprawny MapTable przy późniejszym wykonaniu.
  3. Wykonaj fałszywy opcode nad wyzerowaną pamięcią. Ponieważ wskaźnik tabeli podstawień faktycznie odnosi się do vtable innego opcode’a, bajty wyjściowe stają się leaked niskopozycyjnymi adresami z libimagecodec.quram.so lub jego GOT.
  4. Zastosuj kolejne przejścia MapTable, aby przekształcić te dwubajtowe leaks w offsety prowadzące do gadgetów takich jak __ink_jpeg_enc_process_image+64, QURAMWINK_Read_IO2+124, qpng_check_IHDR+624 i wejścia __system_property_get z libc. Atakujący efektywnie odbudowują pełne adresy wewnątrz swojego sprayed opcode region bez użycia natywnych API do ujawniania pamięci.

Wyzwalanie przejścia JOP ➜ system()

Gdy wskaźniki gadgetów i polecenie shell zostaną zaplanowane wewnątrz spraya opcode’ów:

  1. Ostatnia fala zapisów DeltaPerColumn dodaje 0x0100 do offsetu 0x22 Stage-3 QuramDngImage, przesuwając wskaźnik raw buffer o 0x10000 tak, że teraz wskazuje na łańcuch polecenia atakującego.
  2. Interpreter zaczyna wykonywać końcówkę 1040 opcode’ów Unknown(23). Pierwszy uszkodzony wpis ma swoją vtable podmienioną na sfałszowaną tabelę pod offsetem 0xf000, więc QuramDngOpcode::aboutToApply rozwiązuje qpng_read_data (4. wpis) z fałszywej tabeli.
  3. Złączone gadgety wykonują: ładują wskaźnik QuramDngImage, dodają 0x20, by wskazać na wskaźnik raw buffer, dereferencują go, kopiują wynik do x19/x0, a następnie skaczą przez sloty GOT podmienione na system. Ponieważ wskaźnik raw buffer teraz równa się łańcuchowi atakującego, ostatni gadget wykonuje system(<shell command>) wewnątrz com.samsung.ipservice.

Uwaga na warianty alokatora

Istnieją dwie rodziny payloadów: jedna dostrojona pod jemalloc, druga pod scudo. Różnią się kolejnością bloków opcode’ów w celu osiągnięcia sąsiedztwa, ale dzielą te same logiczne prymitywy (DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). Wyłączona kwarantanna Scudo sprawia, że ponowne użycie freelist dla 0x30 bajtów jest deterministyczne, podczas gdy jemalloc opiera się na kontroli klas rozmiarów poprzez sizing tile/subIFD.

References

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