CVE-2025-64784: DNG SDK 1.7.0 Heap Overflow via Malicious Image File
DNG SDK ≤1.7.0 contains a heap-based buffer overflow in its tile/strip data parsing path. A crafted DNG file can expose sensitive heap memory or crash the host application.
A newly discovered security flaw affects software that opens DNG image files, a format commonly used by professional photographers and camera manufacturers. Think of it like a crack in a security checkpoint that lets someone sneak past guards if they hand over a booby-trapped document.
Here's what's happening: DNG files are essentially containers of image data with specific instructions on how to display them. When certain malicious DNG files are opened, they can overflow the program's memory—imagine filling a bucket beyond its capacity so water spills everywhere. In this case, that "spillage" can let hackers peek at sensitive information your computer is storing nearby in memory, or simply crash your application.
The vulnerability affects Adobe's DNG SDK, which is the underlying code that many photography apps rely on to read these files. This means programs like Lightroom, Capture One, and various other photo editing tools could potentially be vulnerable, depending on which versions they use.
The good news: attackers can't remotely force this to happen. Someone would need to trick you into opening a malicious DNG file—meaning you'd have to consciously download and open it. There's no evidence yet that hackers are actively exploiting this in the wild.
Who should worry most? Professional photographers, photo studios, and anyone regularly opening DNG files from untrusted sources.
What you should actually do: Update your photo editing software when patches become available—software makers are likely already working on fixes. For now, be cautious about opening DNG files from unknown sources or suspicious emails, just like you would with any file attachment. If you don't use DNG files regularly, this probably isn't an urgent concern for you personally.
Want the full technical analysis? Click "Technical" above.
▶ Attack flow — CVE-2025-64784 · Buffer Overflow
Vulnerability Overview
CVE-2025-64784 is a heap-based buffer overflow affecting Adobe's open-source DNG SDK versions 1.7.0 and earlier. The vulnerability lives in the raw image data ingestion pipeline — specifically in the logic that allocates and fills pixel tile or strip buffers from attacker-controlled fields in a DNG/TIFF IFD (Image File Directory). A malformed DNG file can cause the SDK to allocate a buffer sized from one set of IFD tags while copying data sized from a different, larger field, overflowing the heap allocation and corrupting adjacent objects.
CVSS 7.1 (HIGH) reflects the combination of memory disclosure potential and reliable denial-of-service with no authentication barrier — the only prerequisite is that a victim opens the file. Any application embedding the DNG SDK (Lightroom, Camera Raw, third-party raw editors) inherits the exposure.
Root cause: The SDK computes a pixel buffer allocation size from TileWidth × TileLength × SamplesPerPixel × BitsPerSample but copies tile byte data using the raw TileByteCount IFD entry without validating that it fits within the computed allocation, allowing an attacker-controlled count to drive a heap overflow of arbitrary size.
Affected Component
The bug is reachable through dng_tiff_directory::ReadImageData() and its callers in dng_image_writer / dng_read_image, ultimately tracing to the tile-reading helper in dng_stream.cpp and the buffer management in dng_memory.cpp. The SDK is distributed as source and compiled into host applications; there is no shared library boundary.
Affected versions: DNG SDK ≤ 1.7.0
File format entry point: TIFF/DNG IFD tag 0x0144 (TileOffsets), 0x0145 (TileByteCounts), 0x0142 (TileWidth), 0x0143 (TileLength)
Trigger: User opens a malicious .dng file
Root Cause Analysis
The SDK pre-computes expected tile storage from image geometry and then reads each tile's byte count directly from the IFD without cross-checking it against the pre-computed size. Below is reconstructed pseudocode reflecting the SDK's class structure and naming conventions:
// dng_read_image.cpp — reconstructed from DNG SDK 1.7.0 source conventions
// Processes one tile from a tiled DNG/TIFF image.
void dng_read_image::ReadTile(dng_host &host,
dng_ifd &ifd,
dng_stream &stream,
dng_image &image,
uint32 tileIndex)
{
// Allocation size derived from image geometry (attacker-controlled IFD fields)
uint32 tileW = ifd.fTileWidth; // tag 0x0142
uint32 tileH = ifd.fTileLength; // tag 0x0143
uint32 samples = ifd.fSamplesPerPixel; // tag 0x0115
uint32 bitsPerSmp = ifd.fBitsPerSample[0]; // tag 0x0102
// Expected size in bytes — used to size the allocation
uint64 expectedBytes = (uint64)tileW * tileH *
samples * (bitsPerSmp >> 3);
// BUG: tileByteCount comes from tag 0x0145 and is NEVER validated
// against expectedBytes before the copy below.
uint64 tileByteCount = ifd.fTileByteCount[tileIndex]; // attacker-controlled
// Allocation sized to expectedBytes (may be smaller than tileByteCount)
AutoPtr block(host.Allocate(expectedBytes));
uint8 *buf = block->Buffer_uint8();
stream.SetReadPosition(ifd.fTileOffset[tileIndex]);
// BUG: copies tileByteCount bytes into a buffer of expectedBytes.
// If tileByteCount > expectedBytes, heap overflow occurs here.
stream.Get(buf, tileByteCount); // <-- OVERFLOW: no bounds check
// Decompress / unpack pixels from buf ...
DecodeUncompressed(buf, tileByteCount, image, tileIndex, ifd);
}
The mismatch between the allocation path (expectedBytes) and the copy path (tileByteCount) is the complete bug. Both values are derived from attacker-controlled IFD fields with no relationship enforced between them.
Memory Layout
The SDK allocates tile buffers through dng_memory_block, which wraps a raw malloc/new[] call. Adjacent allocations on the heap are typically the next tile's buffer or an internal dng_memory_block header.
// dng_memory.h — dng_memory_block layout (reconstructed)
struct dng_memory_block {
/* +0x00 */ dng_memory_allocator *fAllocator; // back-pointer to allocator
/* +0x08 */ uint32 fLogicalSize; // requested size (expectedBytes)
/* +0x0C */ uint32 fPad; // alignment padding
/* +0x10 */ uint8 fData[]; // pixel data starts here
};
// Total header overhead: 0x10 bytes
// Allocator rounds fLogicalSize up to 16-byte alignment before malloc()
HEAP STATE BEFORE OVERFLOW:
(tile 0 — legitimate, fits)
[ dng_memory_block hdr | fLogicalSize=0x3000 | fData[0x3000] ]
(tile 1 — victim allocation)
[ dng_memory_block hdr | fLogicalSize=0x3000 | fData[0x3000] ]
^--- fAllocator = valid ptr
^--- fLogicalSize = 0x00003000
(next heap object — e.g., dng_linearization_info)
[ dng_linearization_info | fBlackLevel[...] | fWhiteLevel[...] ]
HEAP STATE AFTER OVERFLOW:
(tile 1 read with tileByteCount = 0x5F00, allocated only 0x3000)
[ dng_memory_block hdr | fLogicalSize=0x3000 | fData[0x3000]
+ 0x2F00 bytes OVERFLOW ---------------------> ]
(dng_linearization_info — now corrupted)
[ CORRUPTED: fBlackLevel[0] = attacker bytes ]
[ CORRUPTED: fBlackLevel[1] = attacker bytes ]
[ CORRUPTED: fWhiteLevel = attacker bytes ]
...
On heap info-leak path: corrupted struct fields are read back
and embedded into processed image pixel data → memory disclosure.
Exploitation Mechanics
Two impact classes are realistic here: heap memory disclosure (info leak via pixel output) and denial of service (heap metadata corruption → crash). A full code-execution primitive would require a separate control-flow hijack not demonstrated in this writeup.
EXPLOIT CHAIN — Memory Disclosure (CVE-2025-64784):
1. Craft a DNG file with a tiled IFD:
TileWidth = 0x40 (64 px)
TileLength = 0x30 (48 px)
SamplesPerPixel = 4
BitsPerSample = 8
→ expectedBytes = 64 × 48 × 4 × 1 = 0x3000 (12 288 bytes)
2. Set TileByteCount[0] = 0x5F00 (24 320 bytes — 0x2F00 past allocation)
Set TileOffset[0] = offset to a tile data blob in the file body.
3. Tile data blob: first 0x3000 bytes = arbitrary pixel data (filler).
Remaining 0x2F00 bytes = 0x00 padding (reads heap bytes into "tile").
4. SDK calls stream.Get(buf, 0x5F00) into a 0x3000-byte allocation.
The trailing 0x2F00 bytes read past the tile buffer, pulling bytes
from the adjacent heap object (dng_linearization_info, next tile
block header, allocator metadata, etc.) into the stream buffer.
5. SDK proceeds to DecodeUncompressed() treating all 0x5F00 bytes as
pixel data. The overread heap bytes are packed into output image rows.
6. Host application writes the processed image (JPEG preview, TIFF export).
Attacker reads the output file → heap bytes at known relative offsets
are now embedded in pixel rows, leaking heap pointers and data.
7. With heap pointer leak in hand: ASLR defeated for the host process.
(Full code-exec requires a second primitive — not demonstrated here.)
DENIAL-OF-SERVICE VARIANT:
1–3. Same setup.
4. Set TileByteCount[0] = 0xFFFF0000 — massively oversized.
5. stream.Get() writes far past the tile allocation into allocator
metadata or another object's vtable pointer.
6. Next virtual dispatch (e.g., dng_image::Get()) → null/wild deref.
7. Segmentation fault. Process crashes. Reliable cross-platform DoS.
Patch Analysis
The correct fix enforces that tileByteCount never exceeds the computed allocation before any data is read. A secondary fix caps the allocation itself against a sane maximum to prevent integer-overflow-driven undersized allocations in the geometry calculation.
The ThrowBadFormat() call unwinds via C++ exception through dng_exception, which the host application catches and surfaces as a file-open error — no memory is corrupted, no data is returned.
Detection and Indicators
Static file-level detection is straightforward: a DNG file is malicious if any TileByteCount entry exceeds the corresponding geometry-derived size.
# macOS / Linux: heap corruption crash in dng_stream::Get
Signal: SIGSEGV / SIGABRT
Crashed at: dng_stream::Get(void*, unsigned long)
dng_read_image::ReadTile(...)
dng_read_image::ReadImage(...)
# AddressSanitizer output (instrumented build):
==ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 0x2F00 at offset 0x3000 in region [buf, buf+0x3000)
#0 dng_stream::Get(void*, uint64) dng_stream.cpp
#1 dng_read_image::ReadTile(...) dng_read_image.cpp
#2 dng_read_image::ReadImage(...) dng_read_image.cpp
Shadow bytes around the overflow:
[buf+0x2ff0]: 00 00 00 00 00 00 00 00
[buf+0x3000]: fa fa fa fa fa fa fa fa ← heap redzone begins
[buf+0x3010]: OVERWRITTEN
Remediation
Upgrade immediately to DNG SDK version 1.7.1 or later when Adobe publishes the patched release. Monitor the Adobe Security Bulletins page.
Applications embedding the SDK as source must recompile against the patched SDK — there is no binary-only update path.
Mitigating controls (where patched SDK is not yet available):
Deploy the detection script above in a pre-ingestion pipeline to reject malformed DNG files before they reach the SDK.
Enable heap hardening: compile with -fsanitize=address in QA, and ship with tcmalloc or jemalloc's guard pages in production to convert overflows into hard crashes rather than silent corruption.
Sandbox the DNG-parsing process (seccomp-bpf on Linux, App Sandbox on macOS) so that a successful exploit cannot reach the broader file system or network.
SBOM / dependency audit: Search your software bill of materials for dng_sdk, dng_read_image, or XMP_SDK companion library bundles — many ship the DNG SDK inline without advertising the version.