Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@

- [Android APK Checklist](mobile-pentesting/android-checklist.md)
- [Android Applications Pentesting](mobile-pentesting/android-app-pentesting/README.md)
- [Abusing Android Media Pipelines Image Parsers](mobile-pentesting/android-app-pentesting/abusing-android-media-pipelines-image-parsers.md)
- [Accessibility Services Abuse](mobile-pentesting/android-app-pentesting/accessibility-services-abuse.md)
- [Android Anti Instrumentation And Ssl Pinning Bypass](mobile-pentesting/android-app-pentesting/android-anti-instrumentation-and-ssl-pinning-bypass.md)
- [Android Applications Basics](mobile-pentesting/android-app-pentesting/android-applications-basics.md)
Expand Down
8 changes: 8 additions & 0 deletions src/binary-exploitation/common-exploiting-problems.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ int main(void) {

This workflow lets you triage anti-debug-protected JNI validators quickly, leak secrets when needed, then fuzz only the meaningful bytes, all without touching the original APK.

## Image/Media Parsing Exploits (DNG/TIFF/JPEG)

Malicious camera formats often ship their own bytecode (opcode lists, map tables, tone curves). When a privileged decoder fails to bound-check metadata-derived dimensions or plane indices, those opcodes become attacker-controlled read/write primitives that can groom the heap, pivot pointers, or even leak ASLR. Samsung's in-the-wild Quram exploit is a recent example of chaining a `DeltaPerColumn` bounds bug, heap spraying via skipped opcodes, vtable remapping, and a JOP chain to `system()`.

{{#ref}}
../mobile-pentesting/android-app-pentesting/abusing-android-media-pipelines-image-parsers.md
{{#endref}}

## Pointer-Keyed Hash Table Pointer Leaks on Apple Serialization

### Requirements & attack surface
Expand Down
3 changes: 3 additions & 0 deletions src/mobile-pentesting/android-app-pentesting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ android-enterprise-work-profile-bypass.md
../../linux-hardening/privilege-escalation/android-rooting-frameworks-manager-auth-bypass-syscall-hook.md
{{#endref}}

{{#ref}}
abusing-android-media-pipelines-image-parsers.md
{{#endref}}

{{#ref}}
../../binary-exploitation/linux-kernel-exploitation/arm64-static-linear-map-kaslr-bypass.md
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Abusing Android Media Pipelines & Image Parsers

{{#include ../../banners/hacktricks-training.md}}

## Delivery: Messaging Apps ➜ MediaStore ➜ Privileged Parsers

Modern OEM builds regularly run privileged media indexers that rescan `MediaStore` for "AI" or sharing features. On Samsung firmware prior to the April 2025 patch, `com.samsung.ipservice` loads Quram (`/system/lib64/libimagecodec.quram.so`) and automatically parses any file WhatsApp (or other apps) drops into `MediaStore`. In practice an attacker can send a DNG disguised as `IMG-*.jpg`, wait for the victim to tap "download" (1-click), and the privileged service will parse the payload even if the user never opens the gallery.

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

**Key takeaways**
- Delivery relies on system media re-parsing (not the chat client) and thus inherits that process' permissions (full read/write access to the gallery, ability to drop new media, etc.).
- Any image parser reachable through `MediaStore` (vision widgets, wallpapers, AI résumé features, etc.) becomes remotely reachable if the attacker can convince a target to save media.

## Quram's DNG Opcode Interpreter Bugs

DNG files embed three opcode lists applied at different decode stages. Quram copies Adobe's API, but its Stage-3 handler for `DeltaPerColumn` (opcode ID 11) trusts attacker-supplied plane bounds.

### Failing plane bounds in `DeltaPerColumn`
- Attackers set `plane=5125` and `planes=5123` even though Stage-3 images only expose planes 0–2 (RGB).
- Quram computes `opcode_last_plane = image_planes + opcode_planes` instead of `plane + count`, and never checks whether the resulting plane range fits inside the image.
- The loop therefore writes a delta to `raw_pixel_buffer[plane_index]` with a fully controlled offset (e.g., plane 5125 ⇒ offset `5125 * 2 bytes/pixel = 0x2800`). Each opcode adds a 16-bit float value (0x6666) to the targeted location, yielding a precise heap OOB add primitive.

### Turning increments into arbitrary writes
- The exploit first corrupts Stage-3 `QuramDngImage.bottom/right` using 480 malformed `DeltaPerColumn` operations so future opcodes treat enormous coordinates as in-bounds.
- `MapTable` opcodes (opcode 7) are then aimed at those fake bounds. Using a substitution table of all zeros or a `DeltaPerColumn` with `-Inf` deltas, the attacker zeroes any region, then applies additional deltas to write exact values.
- Because the opcode parameters live inside the DNG metadata, the payload can encode hundreds of thousands of writes without touching process memory directly.

## Heap Shaping Under Scudo

Scudo buckets allocations by size. Quram happens to allocate the following objects with identical 0x30-byte chunk sizes, so they land in the same region (0x40-byte spacing on the heap):
- `QuramDngImage` descriptors for Stage 1/2/3
- `QuramDngOpcodeTrimBounds` and vendor `Unknown` opcodes (ID ≥14, including ID 23)

The exploit sequences allocations to deterministically place chunks:
1. Stage-1 `Unknown(23)` opcodes (20,000 entries) spray 0x30 chunks that later get freed.
2. Stage-2 frees those opcodes and places a new `QuramDngImage` inside the freed region.
3. 240 Stage-2 `Unknown(23)` entries are freed, and Stage-3 immediately allocates its `QuramDngImage` plus a new raw pixel buffer of the same size, reusing those spots.
4. A crafted `TrimBounds` opcode runs first in list 3 and allocates yet another raw pixel buffer before freeing Stage-2 state, guaranteeing "raw pixel buffer ➜ QuramDngImage" adjacency.
5. 640 additional `TrimBounds` entries are marked `minVersion=1.4.0.1` so the dispatcher skips them, but their backing objects stay allocated and later become primitive targets.

This choreography puts the Stage-3 raw buffer immediately before the Stage-3 `QuramDngImage`, so the plane-based overflow flips fields inside the descriptor rather than crashing random state.

## Reusing Vendor "Unknown" Opcodes as Data Blobs

Samsung leaves the high bit set in vendor-specific opcode IDs (e.g., ID 23), which instructs the interpreter to *allocate* the structure but skip execution. The exploit abuses those dormant objects as attacker-controlled heaps:
- Opcode list 1 and 2 `Unknown(23)` entries serve as contiguous scratchpads for storing payload bytes (JOP chain at offset 0xf000 and a shell command at 0x10000 relative to the raw buffer).
- Because the interpreter still treats each object as an opcode when list 3 is processed, commandeering one object's vtable later is enough to start executing attacker data.

## Crafting Bogus `MapTable` Objects & Bypassing ASLR

`MapTable` objects are larger than `TrimBounds`, but once the layout corruption lands, the parser happily reads extra parameters out-of-bounds:
1. Use the linear write primitive to partially overwrite a `TrimBounds` vtable pointer with a crafted `MapTable` substitution table that maps lower 2 bytes from a neighbouring `TrimBounds` vtable to the `MapTable` vtable. Only the low bytes differ between supported Quram builds, so a single 64K lookup table can handle seven firmware versions and every 4 KB ASLR slide.
2. Patch the rest of the `TrimBounds` fields (top/left/width/planes) so the object behaves like a valid `MapTable` when executed later.
3. Execute the fake opcode over zeroed memory. Because the substitution table pointer actually references another opcode's vtable, the output bytes become *leaked* low-order addresses from `libimagecodec.quram.so` or its GOT.
4. Apply additional `MapTable` passes to convert those two-byte leaks into offsets toward gadgets such as `__ink_jpeg_enc_process_image+64`, `QURAMWINK_Read_IO2+124`, `qpng_check_IHDR+624`, and libc's `__system_property_get` entry. The attackers effectively rebuild full addresses inside their sprayed opcode region without native memory disclosure APIs.

## Triggering the JOP ➜ `system()` Transition

Once the gadget pointers and shell command are staged inside the opcode spray:
1. A final wave of `DeltaPerColumn` writes adds `0x0100` to offset 0x22 of the Stage-3 `QuramDngImage`, shifting its raw buffer pointer by 0x10000 so it now references the attacker command string.
2. The interpreter starts executing the tail of 1040 `Unknown(23)` opcodes. The first corrupted entry has its vtable replaced with the forged table at offset 0xf000, so `QuramDngOpcode::aboutToApply` resolves `qpng_read_data` (the 4th entry) out of the fake table.
3. The chained gadgets perform: load the `QuramDngImage` pointer, add 0x20 to point at the raw buffer pointer, dereference it, copy the result into `x19/x0`, then jump through GOT slots rewritten to `system`. Because the raw buffer pointer now equals the attacker string, the final gadget executes `system(<shell command>)` inside `com.samsung.ipservice`.

## Notes on Allocator Variants

Two payload families exist: one tuned for jemalloc, another for scudo. They differ in how opcode blocks are ordered to achieve adjacency but share the same logical primitives (DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). Scudo's disabled quarantine makes 0x30-byte freelist reuse deterministic, while jemalloc relies on size-class control via tile/subIFD sizing.

## References

- [Project Zero – A look at an Android ITW DNG exploit](https://projectzero.google/2025/12/android-itw-dng.html)

{{#include ../../banners/hacktricks-training.md}}