OpenEXR Heap-Based Buffer Overflow in Deep Scanline Parsing via Forged Unpacked SizeGHSA-h45x-qhg2-q375
HIGHFix: AcademySoftwareFoundation/openexr@916cc72GHSA-h45x-qhg2-q375 is a high-severity (CVSS 7.8) CWE-122 vulnerability in openexr. A fix is available for openexr — see the affected versions and patch details below.
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for GHSA-h45x-qhg2-q375.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
openexrReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The OpenEXRCore code is vulnerable to a heap-based buffer overflow during a write operation when decompressing ZIPS-packed deep scan-line EXR files with a maliciously forged chunk header.
Details
When parsing STORAGE_DEEP_SCANLINE chunks from an EXR file, the following code (from src/lib/OpenEXRCore/chunk.c) is used to extract the chunk information:
if (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE)
// SNIP...
cinfo->sample_count_data_offset = dataoff;
cinfo->sample_count_table_size = (uint64_t) ddata[0];
cinfo->data_offset = dataoff + (uint64_t) ddata[0];
cinfo->packed_size = (uint64_t) ddata[1];
cinfo->unpacked_size = (uint64_t) ddata[2];
// SNIP...
By storing this information, the code that will later decompress and reconstruct the chunk bytes, will know how much space the uncompressed data will occupy.
This size is carried along in the chain of decoding/decompression until the undo_zip_impl function in src/lib/OpenEXRCore/internal_zip.c:
static exr_result_t
undo_zip_impl (
exr_decode_pipeline_t* decode,
const void* compressed_data,
uint64_t comp_buf_size,
void* uncompressed_data,
uint64_t uncompressed_size,
void* scratch_data,
uint64_t scratch_size)
{
size_t actual_out_bytes;
exr_result_t res;
if (scratch_size < uncompressed_size) return EXR_ERR_INVALID_ARGUMENT;
res = exr_uncompress_buffer (
decode->context,
compressed_data,
comp_buf_size,
scratch_data,
scratch_size,
&actual_out_bytes);
if (res == EXR_ERR_SUCCESS)
{
decode->bytes_decompressed = actual_out_bytes;
if (comp_buf_size > actual_out_bytes)
res = EXR_ERR_CORRUPT_CHUNK;
else
internal_zip_reconstruct_bytes (
uncompressed_data, scratch_data, actual_out_bytes);
}
return res;
}
The uncompressed_size comes from the unpacked_size extracted earlier, and the uncompressed_data is a buffer allocated by making space for the size "advertised" in the chunk information.
However, scratch_data and actual_out_bytes will contain, after decompression, the uncompressed data and its size, respectively.
The vulnerability lies in the fact that the undo_zip_impl function lacks code to check whether actual_out_bytes is greater than uncompressed_size.
The effect is that, by setting the unpacked_size in the chunk header smaller than the actual chunk decompressed data, it is possible - in the internal_zip_reconstruct_bytes function - to overflow past the boundaries of a heap chunk.
PoC
NOTE: you can download the heap_overflow.exr file from this link:
https://github.com/ShielderSec/poc/tree/main/CVE-2025-48071
- Compile the
exrcheckbinary in a macOS or GNU/Linux machine with ASAN. - Open the
heap_overflow.exrfile with the following command:
exrcheck heap_overflow.exr
- Notice that
exrcheckcrashes with an ASAN stack-trace.
Impact
An attacker might exploit this vulnerability by feeding a maliciously crafted file to a program that uses the OpenEXR libraries, thus gaining the capability to write an arbitrary amount of bytes in the heap. This could potentially result in code execution in the process.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | openexr | ≥ 3.3.0&&< 3.3.3 | 3.3.3pip install --upgrade 'openexr==3.3.3' |
Affected Products
openexropenexrDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for openexr, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update openexr to 3.3.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-h45x-qhg2-q375 is resolved across your whole dependency graph.
Workarounds
Stop feeding it untrusted input: reject or quarantine files and payloads from unverified sources until you can upgrade, restrict accepted formats to the ones you actually need, and run the parsing or decoding step in a least-privileged sandbox or short-lived worker so a crash or corrupted read cannot reach the rest of the process.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
No Red Hat products or offerings are affected by this vulnerability. This vulnerability should be considered Important rather than Moderate because it allows a heap-based buffer overflow during decompression of ZIPS-packed deep scan-line EXR files with a forged chunk header, which directly impacts memory safety. Heap…
Mitigation for this issue is either not available or the currently available options do not meet the Red Hat Product Security criteria comprising ease of use and deployment, applicability to widespread installation base or stability.Source: Red Hat security advisory for GHSA-h45x-qhg2-q375 (CC BY 4.0)
Frequently Asked Questions
Is GHSA-h45x-qhg2-q375 in your dependencies?
Find it across PyPI, including transitive dependencies.