
Глубокий анализ и proof-of-concept для CVE-2026-27280 — записи за пределами буфера в dng_render_task::ProcessArea из Adobe DNG SDK, доступной через API декодирования изображений Android, с описанием первопричины, оценкой эксплуатируемости и анализом разрыва до RCE.
dng_render_task::ProcessArea (Adobe DNG SDK / Android)Достигается из обычного DNG-файла через публичный API декодирования изображений Android и
воспроизведено на Galaxy S25 с использованием собственного /system/lib64/libdng_sdk.so телефона.
DNG_REQUIRE(srcArea.Size() == dstArea.Size()) + индексация srcArea)#12 AImageDecoder_decodeImage /system/lib64/libjnigraphics.so
#11 android::ImageDecoder::decode /system/lib64/libhwui.so
#09 SkCodec::getPixels /system/lib64/libhwui.so
#08 SkRawCodec::onGetPixels /system/lib64/libhwui.so
#07 dng_render::Render() /system/lib64/libdng_sdk.so
#06 SkDngHost::PerformAreaTask /system/lib64/libhwui.so
#00 dng_render_task::ProcessArea /system/lib64/libdng_sdk.so <-- уязвимая
ProcessArea отображает временный буфер размером с тайл поверх srcArea, но индексирует его координатами dstArea.
Поскольку srcArea = dstArea + fSrcOffset, а fSrcOffset — это DefaultCropOrigin файла — ненулевое значение в каждом реальном камеральном DNG — указатель назначения
смещается за пределы выделенной области на fSrcOffset * rowStep * pixelSize, и обратное чтение маски
записывает туда. Это смещение, а не целочисленное переполнение, поэтому не требует огромного выделения памяти:
именно поэтому оно достижимо на телефоне.
01-vulnerability/ уязвимая и исправленная версии dng_render.cpp, а также diff 2471->2502
02-poc/ генераторы, загрузчики, пробы (исходники) + предварительно собранные arm64-бинарники
03-samples/ DNG-файлы
04-evidence/ журналы ASAN, tombstone устройства, вывод проб
05-analysis/ первопричина, генератор, измерения соседей, анализ разрыва для RCE
Работа, выходящая за рамки более раннего вердикта: она опровергает два его утверждения
измерениями, закрывает пробел «используется ли повреждённый указатель» и точно определяет, почему
цепочка останавливается. Сначала читайте 06-rce-lab/README.md; файлы evidence/*.md — это
отчёты по каждому эксперименту, а src/ + apk/ — воспроизводимые стенды.
adb push 02-poc/bin-arm64/poc_aimg 03-samples/trigger_natural_croporigin.dng /data/local/tmp/
adb shell "cd /data/local/tmp && chmod 755 poc_aimg && ./poc_aimg trigger_natural_croporigin.dng"
# -> Segmentation fault, tombstone frames inside libdng_sdk.so
Пересборка arm64-загрузчиков требует собственных флагов dng_sdk от Android (из Android.bp), иначе
структуры не совпадут с поставляемой библиотекой:
-DUNIX_ENV=1 -DqDNGBigEndian=0 -DqDNGThreadSafe=1 -DqDNGUseLibJPEG=1 \
-DqDNGUseXMP=0 -DqDNGValidateTarget=1 -DqDNGUsingSanitizer=1 -DqLinux=1 -DqAndroid=1
Это соответствует CVE-2026-27280 (DNG SDK 1.7.1 2471 и более ранние, CWE-787, CVSS 3.1 7.8 HIGH), опубликовано 2026-03-10 — совпадение по диапазону версий и классу, не подтверждено именованием функции компанией Adobe.
Доказано на реальном пути декодирования Android, на устройстве с патчем до 2026-01-01:
запись за границы буфера с выбираемым атакующим адресом назначения (гранулярность 4 байта, в обеих
направлениях, независимо от ASLR); достижимо из обычного DNG через AImageDecoder,
продемонстрировано падение Samsung Gallery в её загрузчике миниатюр; тихое детерминированное
повреждение соседних объектов; повреждённый указатель, достигающий free() (N1 закрыт);
распыление кучи, выраженное форматом DNG (ImageStats × SubIFD); запись, наведённая на эти
распылённые объекты, 6/10 против 0/10.
Не RCE на пути Android, и по одной корневой причине: код может записать одно-единственное
значение, 0x3F800000. Всё следует из этого — запись непрерывна (нет шага по заголовкам Scudo), нет нуля для записи (нет повреждения длины/счётчика ссылок), и частичная перезапись с сохранением тегов имеет ровно один фиксированный целевой сдвиг. Одно значение — это один выстрел против рандомизированного резервирования в 256 МБ, и никакой объём распыления или конкурентность не добавляют выстрелов. В десктопном SDK набор записываемых значений — 256 значений → 256 выстрелов по полосе ~67 МБ, поэтому оценка 7.8 там корректна.
Отозвано из более раннего вердикта (05-analysis/05-exploitability-verdict.md),
опровергнуто измерениями: «реальный декодер не предлагает примитива распыления» и «перекрытие не управляемо изнутри одного декодирования». Блокировка — не PAC, не BTI и не
контрольные суммы Scudo — это единственное записываемое значение, свойство кода, которое исправление Adobe 2502 устраняет в корне, индексируя через srcArea.
Рабочий эксплойт не создавался, и здесь ничего не публикуется.
| файл | что делает |
|---|
trigger_natural_croporigin.dng | естественная геометрия (DefaultCropOrigin = 8,8); роняет AImageDecoder |
benign_reference.dng | тот же файл с origin 0 — декодируется нормально, используется для зондирования кучи |
silent_corruption.dng | малый класс размера: запись за границы без падения (5/5) |
far_write_crash.dng | большое смещение: заметный сбой |
| файл | содержимое |
|---|
01-root-cause.md | ошибка, исправление, форма записи |
02-dng-generator.md | как строится триггерный файл, две ручки настройки, подводные камни |
03-neighbours.md | показатели управляемости, что находится рядом с буфером, смягчающие меры |
04-what-is-missing-for-rce.md | что доказано (P1-P8), что нет (N1-N3), оставшиеся шаги |
05-exploitability-verdict.md | более ранний вердикт — заменён 06-rce-lab, сохранён для протокола |
| файл | содержимое |
|---|
EXP1-stride-vs-scudo.md | шаг против заголовков Scudo: возможно в абстракции, невозможно на пути Android (planes==1) |
EXP2-N1-corrupted-pointer-used.md | повреждённый указатель достигает free() — N1 закрыт |
EXP2-real-process.md | запись в реальном процессе Samsung Gallery, ничего не подсажено |
EXP-imagestats-spray.md | распыление кучи, которое более ранний вердикт называл невозможным, выраженное форматом DNG |
EXP-aimed-spray.md | наведение записи на распылённые объекты: 6/10 против 0/10 |
EXP-small-class-in-app.md | указатели на код в пределах досягаемости внутри реального процесса приложения |
EXP-band-commit.md / EXP-concurrency.md | почему фиксация цели не удаётся: одно записываемое значение = один выстрел |
EXP-write-read-mismatch.md | второй дефект + полный аудит исходного кода ProcessArea |
EXP-value-1.0f.md | путь 1.0f-как-данные, закрыт структурно |