
Tiefgehende Analyse und Proof-of-Concept für CVE-2026-27280, einen Out-of-Bounds-Schreibzugriff in dng_render_task::ProcessArea des Adobe DNG SDK, erreichbar über die Bilddecodierungs-API von Android, mit Root-Cause-Analyse, Bewertung der Ausnutzbarkeit und RCE-Lückenanalyse.
dng_render_task::ProcessArea (Adobe DNG SDK / Android)Erreichbar über eine gewöhnliche DNG-Datei durch Androids öffentliche Bilddecodierungs-API und
reproduziert auf einem Galaxy S25 mit der eigenen /system/lib64/libdng_sdk.so des Telefons.
DNG_REQUIRE(srcArea.Size() == dstArea.Size()) + srcArea-Indizierung)#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 <-- verwundbar
ProcessArea bildet einen kachelgroßen temporären Puffer auf srcArea ab, indiziert ihn aber mit dstArea-Koordinaten.
Da srcArea = dstArea + fSrcOffset und fSrcOffset der DefaultCropOrigin der Datei ist — in jedem echten Kamera-DNG ungleich Null — wird der Zielzeiger um fSrcOffset * rowStep * pixelSize aus der Allokation verschoben, und das Masken-Rücklesen schreibt dorthin. Es handelt sich um eine Verschiebung, nicht um einen Integer-Überlauf, daher ist keine riesige Allokation nötig: Deshalb ist es auf einem Telefon erreichbar.
01-vulnerability/ die verwundbaren und behobenen Versionen von dng_render.cpp sowie der Diff 2471->2502
02-poc/ Generatoren, Lader, Sonden (Quellen) + vorgebaute arm64-Binaries
03-samples/ die DNG-Dateien
04-evidence/ ASAN-Protokolle, Geräte-Tombstones, Sondenausgabe
05-analysis/ Ursachenanalyse, Generator, Nachbarnessungen, RCE-Lückenanalyse
Die Arbeit, die über das frühere Urteil hinausgeht: Sie widerlegt zwei seiner Behauptungen durch
Messung, schließt die Lücke „wird ein korrupter Zeiger verwendet" und bestimmt genau, warum die
Kette stoppt. Zuerst 06-rce-lab/README.md lesen; die evidence/*.md-Dateien sind die
Dokumentationen pro Experiment, und src/ + apk/ sind die reproduzierbaren Testumgebungen.
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 innerhalb von libdng_sdk.so
Das Neubauen der arm64-Lader erfordert Androids eigene dng_sdk-Flags (aus Android.bp), sonst
stimmen die Strukturlayouts nicht mit der ausgelieferten Bibliothek überein:
-DUNIX_ENV=1 -DqDNGBigEndian=0 -DqDNGThreadSafe=1 -DqDNGUseLibJPEG=1 \
-DqDNGUseXMP=0 -DqDNGValidateTarget=1 -DqDNGUsingSanitizer=1 -DqLinux=1 -DqAndroid=1
Dies entspricht CVE-2026-27280 (DNG SDK 1.7.1 2471 und früher, CWE-787, CVSS 3.1 7.8 HOCH), veröffentlicht am 2026-03-10 — abgeglichen über Versionsbereich und Klasse, nicht durch Benennung der Funktion durch Adobe bestätigt.
Bewiesen, auf dem echten Android-Decodierungspfad, auf einem auf 2026-01-01 gepatchten Gerät:
Schreibfehler außerhalb der Grenzen mit vom Angreifer gewähltem Ziel (4-Byte-Granularität, in beide
Richtungen, ASLR-unabhängig); erreichbar aus einer einfachen DNG über AImageDecoder,
demonstriert durch das Abstürzen der Samsung Gallery in ihrem Thumbnail-Lader; stille deterministische
Korruption von Nachbarn; ein korrupter Zeiger, der free() erreicht (N1 geschlossen); ein
Heap-Spray, ausgedrückt durch das DNG-Format (ImageStats × SubIFD); der Schreibvorgang gezielt auf
diese gesprayten Objekte, 6/10 vs. 0/10.
Kein RCE, auf dem Android-Pfad, und aus einem einzigen Grund: Der Code kann einen einzelnen
Wert schreiben, 0x3F800000. Alles folgt daraus — der Schreibvorgang ist zusammenhängend (keine Schrittweite
über Scudo-Header), es gibt keine Null zu schreiben (keine Längen-/Refcount-Korruption), und die
tag-erhaltende partielle Überschreibung hat genau einen festen Ziel-Offset. Ein Wert ist
ein Versuch gegen eine randomisierte 256-MB-Reservierung, und keine Menge an Spray-Volumen oder
Nebenläufigkeit fügt Versuche hinzu. Auf dem Desktop-SDK beträgt die beschreibbare Menge 256 Werte → 256 Versuche
über ein ~67-MB-Band, weshalb die 7.8 dort korrekt ist.
Zurückgezogen aus dem früheren Urteil (05-analysis/05-exploitability-verdict.md),
durch Messung widerlegt: „ein echter Decoder bietet keine Spray-Primitive" und „die
Überlappung ist von innerhalb eines einzelnen Decodierens nicht steuerbar". Der Block ist nicht PAC, BTI oder
Scudos Prüfsummen — es ist der einzelne beschreibbare Wert, eine Eigenschaft des Codes, die Adobes
2502-Fix an der Wurzel entfernt, indem er mit srcArea indiziert.
Es wurde kein funktionierender Exploit gebaut, und hier wird keiner veröffentlicht.
| Datei | Funktion |
|---|
trigger_natural_croporigin.dng | natürliche Geometrie (DefaultCropOrigin = 8,8); stürzt AImageDecoder ab |
benign_reference.dng | dieselbe Datei mit Ursprung 0 — decodiert einwandfrei, dient zur Heap-Sondierung |
silent_corruption.dng | kleine Größenklasse: schreibt außerhalb der Grenzen ohne Absturz (5/5) |
far_write_crash.dng | große Verschiebung: stürzt sichtbar ab |
| Datei | Inhalt |
|---|
01-root-cause.md | der Fehler, der Fix, die Form des Schreibvorgangs |
02-dng-generator.md | wie die Trigger-Datei aufgebaut wird, die zwei Stellschrauben, Fallstricke |
03-neighbours.md | Steuerbarkeitszahlen, was neben dem Puffer liegt, Gegenmaßnahmen |
04-what-is-missing-for-rce.md | was bewiesen ist (P1-P8), was nicht (N1-N3), die verbleibenden Schritte |
05-exploitability-verdict.md | das frühere Urteil — durch 06-rce-lab ersetzt, zur Dokumentation aufbewahrt |
| Datei | Inhalt |
|---|
EXP1-stride-vs-scudo.md | Schrittweite vs. Scudo-Header: abstrakt möglich, auf dem Android-Pfad unmöglich (planes==1) |
EXP2-N1-corrupted-pointer-used.md | ein korrupter Zeiger erreicht free() — N1 geschlossen |
EXP2-real-process.md | der Schreibvorgang im echten Samsung-Gallery-Prozess, nichts manipuliert |
EXP-imagestats-spray.md | das Heap-Spray, das das frühere Urteil für unmöglich hielt, ausgedrückt durch das DNG-Format |
EXP-aimed-spray.md | den Schreibvorgang auf gesprayte Objekte ausrichten: 6/10 vs. 0/10 |
EXP-small-class-in-app.md | Codepointer in Reichweite innerhalb eines echten App-Prozesses |
EXP-band-commit.md / EXP-concurrency.md | warum das Committen des Ziels fehlschlägt: ein beschreibbarer Wert = ein Versuch |
EXP-write-read-mismatch.md | ein zweiter Defekt + die vollständige Quellcode-Auditierung von ProcessArea |
EXP-value-1.0f.md | der 1.0f-als-Datum-Weg, strukturell geschlossen |