Зловживання медіа-конвеєрами Android та парсерами зображень

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Доставка: додатки обміну повідомленнями ➜ MediaStore ➜ привілейовані парсери

Сучасні OEM build-и регулярно запускають привілейовані індексатори медіа, які повторно сканують MediaStore для функцій “AI” або обміну. У прошивках Samsung до патчу квітня 2025 року com.samsung.ipservice завантажує Quram (/system/lib64/libimagecodec.quram.so) і автоматично парсить будь-який файл, який WhatsApp (або інші додатки) поміщають у MediaStore. На практиці атакуючий може надіслати DNG, замаскований як IMG-*.jpg, зачекати, поки жертва натисне “download” (1-click), і привілейована служба розпарсить payload навіть якщо користувач ніколи не відкриває галерею.

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

Ключові висновки

  • Доставка покладається на повторний парсинг системних медіа (не на чат-клієнт) і тому успадковує дозволи цього процесу (повний доступ для читання/запису до галереї, можливість додавати нові медіа тощо).
  • Будь-який image parser, до якого можна дістатися через MediaStore (vision widgets, wallpapers, AI résumé features тощо), стає віддалено доступним, якщо нападник переконає ціль зберегти медіа.

0-click DD+/EAC-3 decoding path (Google Messages ➜ mediacodec sandbox)

Сучасні messaging stacks також авто-декодують audio для транскрипції/пошуку. На Pixel 9, Google Messages передає вхідний RCS/SMS audio до Dolby Unified Decoder (UDC) у /vendor/lib64/libcodec2_soft_ddpdec.so перш ніж користувач відкриє повідомлення, розширюючи 0-click поверхню до медіа codec-ів.

Ключові обмеження парсингу

  • Кожен DD+ syncframe має до 6 блоків; кожен блок може скопіювати до 0x1FF байт керованих нападником skip data у skip buffer (≈ 0x1FF * 6 байт на фрейм).
  • skip buffer сканується на EMDF: syncword (0xX8) + emdf_container_length (16b) + поля змінної довжини. emdf_payload_size парситься за допомогою необмеженого циклу variable_bits(8).
  • EMDF payload bytes виділяються в кастомному per-frame “evo heap” bump-алокаторі, а потім копіюються по байту з bit-reader, обмеженого emdf_container_length.

Integer-overflow → heap-overflow примітива (CVE-2025-54957)

  • ddp_udc_int_evo_malloc вирівнює alloc_size+extra до 8 байт через total_size += (8 - total_size) % total_size без перевірки wrap. Значення поблизу 0xFFFFFFFFFFFFFFF9..FF стискаються до крихітного total_size на AArch64.
  • Цикл копіювання все ще використовує logical payload_length з emdf_payload_size, тож байти нападника перезаписують дані evo-heap поза зменшеним чанком.
  • Довжина переповнення точно обмежена обраним нападником emdf_container_length; байти переповнення контролюються EMDF payload. slab-алокатор скидається кожен syncframe, що дає передбачувану суміжність.

Secondary read примітива Якщо emdf_container_length > skipl, парсинг EMDF читає за межі ініціалізованих skip байтів (OOB read). Саме по собі це виливає нулі/відомі медіа, але після пошкодження суміжної heap-метаданих воно може прочитати назад пошкоджену ділянку для валідації експлойту.

Exploitation recipe

  1. Сконструювати EMDF з великим emdf_payload_size (через variable_bits(8)), щоб padding алокатора обернувся у малий chunk.
  2. Встановити emdf_container_length на бажану довжину переповнення (≤ загального бюджету skip data); помістити байти переповнення в EMDF payload.
  3. Сформувати per-frame evo heap так, щоб мала алокація опинилася перед цільовими структурами всередині статичного буфера декодера (≈693 KB) або динамічного буфера (≈86 KB), який виділяється один раз на екземпляр декодера.
  4. Опційно вибрати emdf_container_length > skipl, щоб після корупції прочитати назад перезаписані дані зі skip buffer.

Quram’s DNG Opcode Interpreter Bugs

DNG файли вбудовують три списки opcode-ів, що застосовуються на різних стадіях декоду. Quram копіює Adobe API, але його Stage-3 handler для DeltaPerColumn (opcode ID 11) довіряє attacker-supplied bounds для plane.

Failing plane bounds in DeltaPerColumn

  • Нападники ставлять plane=5125 і planes=5123 хоча Stage-3 зображення експонують лише planes 0–2 (RGB).
  • Quram обчислює opcode_last_plane = image_planes + opcode_planes замість plane + count, і ніколи не перевіряє, чи поміщається отриманий діапазон plane в межі зображення.
  • Тому цикл записує delta в raw_pixel_buffer[plane_index] з повністю керованим зсувом (наприклад, plane 5125 ⇒ зсув 5125 * 2 bytes/pixel = 0x2800). Кожен opcode додає 16-bit float значення (0x6666) до цільової локації, даючи точну heap OOB add-примітиву.

Turning increments into arbitrary writes

  • Експлойт спочатку коруптує Stage-3 QuramDngImage.bottom/right за допомогою 480 malformed DeltaPerColumn операцій, так що майбутні opcode-и трактують величезні координати як in-bounds.
  • MapTable opcode-и (opcode 7) потім спрямовуються на ці фейкові bounds. Використовуючи substitution table з усіх нулів або DeltaPerColumn з deltas -Inf, нападник знуляє будь-яку ділянку, а потім застосовує додаткові deltas для запису точних значень.
  • Оскільки параметри opcode-ів живуть в DNG metadata, payload може кодувати сотні тисяч записів без прямого маніпулювання пам’яттю процесу.

Heap Shaping Under Scudo

Scudo групує алокації за розміром. Quram випадково виділяє такі об’єкти з ідентичним розміром chunk 0x30, тож вони потрапляють у ту ж область (0x40-байтні інтервали в heap):

  • QuramDngImage дескриптори для Stage 1/2/3
  • QuramDngOpcodeTrimBounds та vendor Unknown opcode-и (ID ≥14, включаючи ID 23)

Експлойт послідовно виділяє, щоб детерміновано розмістити чанки:

  1. Stage-1 Unknown(23) opcode-и (20,000 записів) спреять 0x30 чанків, які пізніше звільняються.
  2. Stage-2 звільняє ті opcode-и і поміщає новий QuramDngImage всередину звільненої області.
  3. 240 Stage-2 Unknown(23) записів звільняються, і Stage-3 одразу алокує свій QuramDngImage плюс новий raw pixel buffer того ж розміру, повторно використовуючи ці місця.
  4. Сконструйований TrimBounds opcode виконується першим у списку 3 і алокує ще один raw pixel buffer перш ніж звільнити Stage-2 стан, гарантуючи суміжність “raw pixel buffer ➜ QuramDngImage”.
  5. Додаткові 640 TrimBounds записів позначені minVersion=1.4.0.1, тому диспетчер їх пропускає, але їхні backing objects лишаються алокованими і пізніше стають цільовими примітивами.

Ця хореографія ставить Stage-3 raw buffer безпосередньо перед Stage-3 QuramDngImage, тож plane-based overflow змінює поля всередині дескриптора замість того, щоб аварійно падати на випадковий стан.

Reusing Vendor “Unknown” Opcodes as Data Blobs

Samsung залишає high bit встановленим у vendor-specific opcode IDs (наприклад, ID 23), що інструктує інтерпретатор алокувати структуру, але пропустити виконання. Експлойт зловживає цими dormant об’єктами як attacker-controlled heap:

  • Opcode list 1 і 2 Unknown(23) записи служать як суміжні scratchpad-и для зберігання payload байтів (JOP chain на offset 0xf000 і shell command на 0x10000 відносно raw buffer).
  • Оскільки інтерпретатор все ще трактує кожний об’єкт як opcode коли обробляється list 3, підкоригувати vtable одного об’єкта потім достатньо, щоб почати виконувати дані нападника.

Crafting Bogus MapTable Objects & Bypassing ASLR

MapTable об’єкти більші за TrimBounds, але коли корупція layout відбувається, парсер охоче читає додаткові параметри OOB:

  1. Використати linear write-примітив для часткового перезапису TrimBounds vtable pointer скрафченою MapTable substitution table, яка відображає нижчі 2 байти з сусіднього TrimBounds vtable до MapTable vtable. Лише low bytes відрізняються між підтримуваними Quram збірками, тож одна 64K lookup таблиця може покрити сім firmware версій і кожен 4 KB ASLR slide.
  2. Заштопати решту полів TrimBounds (top/left/width/planes), щоб об’єкт поводився як валідний MapTable при його виконанні пізніше.
  3. Виконати фейковий opcode над знеціненим (zeroed) пам’яттю. Оскільки pointer substitution table фактично посилається на vtable іншого opcode-а, вихідні байти стають leaked низько-ординатними адресами з libimagecodec.quram.so або його GOT.
  4. Застосувати додаткові проходи MapTable, щоб перетворити ці двобайтові leaks в офсети до гаджетів, таких як __ink_jpeg_enc_process_image+64, QURAMWINK_Read_IO2+124, qpng_check_IHDR+624, і libc-ний __system_property_get entry. Нападники фактично відбудовують повні адреси всередині свого спреєного opcode регіону без нативних API для memory disclosure.

Triggering the JOP ➜ system() Transition

Коли вказівники гаджетів і shell command розміщені всередині opcode spray:

  1. Остання хвиля DeltaPerColumn записів додає 0x0100 до офсету 0x22 Stage-3 QuramDngImage, зсуваючи його raw buffer pointer на 0x10000 так, що він тепер вказує на attacker command string.
  2. Інтерпретатор починає виконувати хвіст з 1040 Unknown(23) opcode-ів. Перший корумпований запис має vtable замінену на підроблену таблицю на offset 0xf000, тож QuramDngOpcode::aboutToApply резолвить qpng_read_data (4-й запис) з фейкової таблиці.
  3. Ланцюжок гаджетів виконує: завантажує pointer QuramDngImage, додає 0x20 щоб вказати на raw buffer pointer, дерефенсує його, копіює результат в x19/x0, потім переходить через GOT слоти перезаписані на system. Оскільки raw buffer pointer тепер рівний attacker string, фінальний гаджет виконує system(<shell command>) всередині com.samsung.ipservice.

Notes on Allocator Variants

Існує дві сім’ї payload-ів: одна налаштована для jemalloc, інша для scudo. Вони відрізняються порядком блоків opcode-ів для досягнення суміжності, але поділяють ті ж логічні примітиви (DeltaPerColumn bug ➜ MapTable zero/write ➜ bogus vtable ➜ JOP). Scudo з вимкненою quarantine робить повторне використання freelist для 0x30-байтних чанків детермінованим, тоді як jemalloc покладається на контроль size-class через tile/subIFD sizing.

References

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks