Missbrauch von Android-Media-Pipelines & Image-Parsern

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Zustellung: Messaging-Apps ➜ MediaStore ➜ Privilegierte Parser

Moderne OEM-Builds führen regelmäßig privilegierte Media-Indexer aus, die MediaStore für “AI”- oder Sharing-Funktionen neu scannen. Auf Samsung-Firmware vor dem Patch vom April 2025 lädt com.samsung.ipservice Quram (/system/lib64/libimagecodec.quram.so) und parst automatisch jede Datei, die WhatsApp (oder andere Apps) in MediaStore ablegt. In der Praxis kann ein Angreifer eine DNG senden, die als IMG-*.jpg getarnt ist, darauf warten, dass das Opfer auf “download” tippt (1-click), und der privilegierte Dienst parst die Nutzlast, selbst wenn der Benutzer die Galerie nie öffnet.

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

Wichtigste Erkenntnisse

  • Die Auslieferung beruht auf dem erneuten Parsen von System-Medien (nicht dem Chat-Client) und erbt daher die Berechtigungen dieses Prozesses (voller Lese/Schreib-Zugriff auf die Galerie, Möglichkeit, neue Medien abzulegen usw.).
  • Jeder Image-Parser, der über MediaStore erreichbar ist (vision widgets, wallpapers, AI résumé features, etc.), wird aus der Ferne erreichbar, wenn ein Angreifer ein Ziel dazu bringt, Medien zu speichern.

Quram’s DNG Opcode Interpreter Fehler

DNG-Dateien betten drei Opcode-Listen ein, die in verschiedenen Decode-Stufen angewendet werden. Quram kopiert Adobes API, aber sein Stage-3-Handler für DeltaPerColumn (opcode ID 11) vertraut attacker-supplied plane bounds.

Fehlerhafte Ebenengrenzen in DeltaPerColumn

  • Angreifer setzen plane=5125 und planes=5123, obwohl Stage-3-Bilder nur Ebenen 0–2 (RGB) ausliefern.
  • Quram berechnet opcode_last_plane = image_planes + opcode_planes anstatt plane + count und prüft nie, ob der resultierende Ebenenbereich ins Bild passt.
  • Die Schleife schreibt daher ein Delta in raw_pixel_buffer[plane_index] mit einem vollständig kontrollierten Offset (z. B. plane 5125 ⇒ Offset 5125 * 2 bytes/pixel = 0x2800). Jeder Opcode addiert einen 16-Bit-Float-Wert (0x6666) an die Zieladresse und ergibt damit ein präzises heap OOB add-Primitive.

Inkremente in beliebige Writes verwandeln

  • Der Exploit korruptiert zuerst QuramDngImage.bottom/right von Stage-3 mittels 480 fehlerhafter DeltaPerColumn-Operationen, sodass zukünftige Opcodes riesige Koordinaten als in-bounds behandeln.
  • MapTable-Opcodes (opcode 7) zielen dann auf diese gefälschten Grenzen. Mit einer Substitutionstabelle aus Nullen oder einem DeltaPerColumn mit -Inf-Deltas nullt der Angreifer beliebige Regionen und wendet anschließend weitere Deltas an, um exakte Werte zu schreiben.
  • Weil die Opcode-Parameter innerhalb der DNG-Metadaten leben, kann das Payload Hunderte von Tausenden von Writes kodieren, ohne direkt in den Prozessspeicher zu schreiben.

Heap Shaping Under Scudo

Scudo bucktet Allocations nach Größe. Quram allokiert zufällig die folgenden Objekte mit identischen 0x30-Byte-Chunks, so dass sie im selben Bereich landen (0x40-Byte-Abstand auf dem heap):

  • QuramDngImage descriptors für Stage 1/2/3
  • QuramDngOpcodeTrimBounds und vendor Unknown opcodes (ID ≥14, einschließlich ID 23)

Der Exploit sequenziert Allokationen, um Chunks deterministisch zu platzieren:

  1. Stage-1 Unknown(23) opcodes (20.000 Einträge) sprayen 0x30-Chunks, die später freigegeben werden.
  2. Stage-2 freed diese opcodes und platziert ein neues QuramDngImage in der freigegebenen Region.
  3. 240 Stage-2 Unknown(23)-Einträge werden freed, und Stage-3 allokiert sofort sein QuramDngImage plus einen neuen raw pixel buffer derselben Größe, wodurch diese Plätze wiederverwendet werden.
  4. Ein konstruiertes TrimBounds-Opcode läuft zuerst in Liste 3 und allokiert noch einen raw pixel buffer, bevor Stage-2-State freigegeben wird, und garantiert so die Adjazenz “raw pixel buffer ➜ QuramDngImage”.
  5. 640 zusätzliche TrimBounds-Einträge sind mit minVersion=1.4.0.1 markiert, sodass der Dispatcher sie überspringt, ihre Backing-Objekte jedoch alloziert bleiben und später als primitive Ziele dienen.

Diese Choreographie platziert den Stage-3 raw buffer unmittelbar vor dem Stage-3 QuramDngImage, sodass der ebenenbasierte Overflow Felder innerhalb des Descriptors umschreibt, anstatt zufällige States abstürzen zu lassen.

Vendor “Unknown” Opcodes als Data Blobs wiederverwenden

Samsung lässt das high bit in vendor-spezifischen opcode IDs gesetzt (z. B. ID 23), was den Interpreter anweist, die Struktur zu allocaten, aber die Ausführung zu überspringen. Der Exploit missbraucht diese ruhenden Objekte als vom Angreifer kontrollierte Heaps:

  • Opcode-Listen 1 und 2 Unknown(23)-Einträge dienen als zusammenhängende Scratchpads zum Speichern von Payload-Bytes (JOP chain bei Offset 0xf000 und ein Shell-Kommando bei 0x10000 relativ zum raw buffer).
  • Weil der Interpreter jedes Objekt beim Verarbeiten von Liste 3 weiterhin als Opcode betrachtet, reicht es, später die vtable eines Objekts zu übernehmen, um die Ausführung von Angreifer-Daten zu starten.

Gefälschte MapTable-Objekte konstruieren & ASLR umgehen

MapTable-Objekte sind größer als TrimBounds, aber sobald die Layout-Korruption sitzt, liest der Parser gerne zusätzliche Parameter OOB:

  1. Verwende das lineare Write-Primitive, um teilweise einen TrimBounds-vtable-Pointer mit einer gefälschten MapTable-Substitutionstabelle zu überschreiben, die die unteren 2 Bytes von einer benachbarten TrimBounds-vtable auf die MapTable-vtable abbildet. Nur die low bytes unterscheiden sich zwischen unterstützten Quram-Builds, sodass eine einzige 64K-Lookuptabelle sieben Firmware-Versionen und jede 4 KB ASLR-Slide abdecken kann.
  2. Patch die restlichen TrimBounds-Felder (top/left/width/planes), sodass das Objekt sich später wie eine gültige MapTable verhält.
  3. Führe den gefälschten Opcode über nullisierten Speicher aus. Weil der Substitutionstabelle-Pointer tatsächlich auf die vtable eines anderen Opcodes referenziert, werden die Output-Bytes zu leaked low-order addresses aus libimagecodec.quram.so oder dessen GOT.
  4. Wende zusätzliche MapTable-Durchläufe an, um diese Zwei-Byte-Leaks in Offsets zu Gadgets wie __ink_jpeg_enc_process_image+64, QURAMWINK_Read_IO2+124, qpng_check_IHDR+624 und libc’s __system_property_get-Eintritt zu konvertieren. Die Angreifer bauen damit effektiv vollständige Adressen innerhalb ihrer gesprayten Opcode-Region ohne native Memory-Disclosure-APIs zusammen.

Triggering the JOP ➜ system() Transition

Sobald Gadget-Pointer und Shell-Kommando im Opcode-Spray platziert sind:

  1. Eine letzte Welle von DeltaPerColumn-Writes addiert 0x0100 an Offset 0x22 des Stage-3 QuramDngImage und verschiebt dessen raw buffer pointer um 0x10000, sodass er nun auf den Angreifer-Command-String zeigt.
  2. Der Interpreter beginnt, die Tail von 1040 Unknown(23)-Opcodes auszuführen. Der erste korrumpierte Eintrag hat seine vtable durch die gefälschte Tabelle bei Offset 0xf000 ersetzt, sodass QuramDngOpcode::aboutToApply qpng_read_data (den 4. Eintrag) aus der Fake-Tabelle auflöst.
  3. Die verketteten Gadgets führen aus: Laden des QuramDngImage-Pointers, Addieren von 0x20, um auf den raw buffer pointer zu zeigen, Dereferenzieren desselben, Kopieren des Ergebnisses in x19/x0, dann Springen durch GOT-Slots, die auf system umgeschrieben wurden. Weil der raw buffer pointer jetzt gleich dem Angreifer-String ist, führt das finale Gadget system(<shell command>) innerhalb von com.samsung.ipservice aus.

Hinweise zu Allocator-Varianten

Es existieren zwei Payload-Familien: eine auf jemalloc abgestimmt, eine andere auf scudo. Sie unterscheiden sich darin, wie Opcode-Blöcke angeordnet werden, um Adjazenz zu erreichen, teilen aber dieselben logischen Primitiven (DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). Scudos deaktivierte Quarantine macht die Wiederverwendung von 0x30-Byte-Freelists deterministisch, während jemalloc auf Size-Class-Control via tile/subIFD-Sizing angewiesen ist.

References

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks