Pillow Multi-Frame Image Loaders: Stale Native Buffers Enable Memory Corruption and Demonstrated RCE (CVE-2026-96639)
What Happened
A GitHub Security Advisory (GHSA-v9pv-5gc8-w2m6, CVE-2026-96639) reports a vulnerability in the Python imaging library Pillow affecting multi-frame image loaders. According to the researcher (credited as Rotem Kama), Pillow's ImageFile.load_prepare() only reallocates native ImagingCore storage when _im is None — it does not verify that retained storage still matches the current frame's mode and size. Several multi-frame formats (PSD, MPO/JPEG, DCX, and MIC) can change a frame's mode (e.g., from L to RGB/CMYK) without invalidating _im, causing the decoder to write wider pixel data into a narrower, stale native buffer.
The report states this was verified on Pillow 12.3.0 and produces native out-of-bounds writes, over-reads, and crashes. For the MPO/JPEG path specifically, the researcher reports demonstrating command execution via Pillow's documented ImageSequence.Iterator + frame.load() pattern, using a fixed, non-weaponized harmless marker command (no reverse shell, persistence, or exfiltration) that reportedly succeeded 10/10 times in tested environments, including a local simulated web-upload scenario. The advisory lists CVSS 9.8 and exploit status as PoC-public.
Why It Matters
Pillow is one of the most widely used image-processing libraries in the Python ecosystem, commonly embedded in web upload handlers, thumbnailing services, and document/image pipelines. If accurate, the described server-side scenario — an unauthenticated image upload automatically processed by Pillow, requiring a single HTTP request and no user interaction — represents a high-severity remote attack surface for any service that decodes untrusted multi-frame images (PSD, MPO, DCX, or MIC) with affected code paths. The researcher notes TIFF is unaffected because it already enforces a mode/size guard before reusing storage, which is a useful signal for understanding the gap in the other formats.
What Defenders Should Watch For Now
- Inventory where Pillow is used to process untrusted, user-supplied images — particularly upload endpoints, thumbnail generators, and document-conversion pipelines — and check Pillow version in use.
- Pay particular attention to any code that iterates multi-frame images via
ImageSequence.Iteratoror calls.seek()/.load()repeatedly on PSD, MPO/JPEG, DCX, or MIC files. - Monitor image-processing services for crashes, unexpected worker restarts, or anomalous memory/process behavior when handling multi-frame image uploads, as this may indicate exploitation attempts rather than benign malformed files.
- Consider sandboxing or isolating image-decoding workloads (e.g., containerized, low-privilege, resource-limited workers) as defense-in-depth regardless of patch status, since this class of native-memory bug in a decoding library is a recurring risk pattern.
- Track upstream Pillow guidance and apply a fixed release once the maintainers publish one; the advisory proposes a central guard in
load_prepare()comparing native storage mode/size against the logical image mode/size, or explicit_iminvalidation in each mode-changingseek()/_open()path.
Developing Intel
This is a same-day advisory and the details above reflect the researcher's report as published; independent confirmation, an official patched Pillow release, and further community analysis are still pending. Treat specifics — especially the RCE demonstration — as reported claims rather than fully independently verified facts until Pillow maintainers respond. For full technical details, reproduction notes, and updates, see the original advisory: GHSA-v9pv-5gc8-w2m6.