On this page (7 sections)
Pillow, the widely used Python imaging library, has a critical memory-safety flaw in how it handles multi-frame images. If a later frame of a PSD, MPO, DCX or MIC file switches to a wider pixel mode, Pillow can write that data into a buffer sized for the earlier, narrower frame. The result is a crash at minimum, and in the MPO path the reporter demonstrated command execution in matching runtime setups.
Anyone running a Python web application, API or worker that opens user-supplied images with Pillow should check their version. The fix is in Pillow 13.0.0. So far the only coverage of this issue is the project's own advisory, release and patch, so treat details beyond those as unconfirmed.
What happened
The Pillow maintainers published a GitHub security advisory describing a problem in several multi-frame loaders. The same text is mirrored in the GitHub Advisory Database entry. The fix shipped in Pillow 13.0.0, released on 16 October according to the release page, with the code change in this commit. The CVE record lists a publication date of 2026-10-21.
How the bug works
When Pillow decodes an image, it keeps a native memory buffer sized for the current frame's pixel mode and dimensions. In a multi-frame file, moving to the next frame can change the mode (for example from single-channel grayscale to RGB or CMYK) while keeping the same width and height. According to the advisory, the function that prepares decoding only allocates fresh storage when no buffer exists. It does not check that an existing buffer still matches the frame's mode and size.
So if frame 0 allocated a narrow buffer and frame 1 declares a wider mode, the decoder writes wider pixels into the too-small buffer. That is a classic out-of-bounds write, the kind of memory corruption that usually crashes the process and can sometimes be steered further.
Affected formats
- PSD: seeking can switch from grayscale storage to RGB layer decoding while keeping the old buffer. The reporter describes attacker-controlled out-of-bounds writes and reliable crashes.
- MPO (a JPEG-based format): a crafted two-frame file can make a later CMYK or RGB frame decode into retained grayscale storage. This path can also cause an out-of-bounds read.
- DCX: seeking calls the PCX opener, which changes mode and tile state without a guard. The reporter reports a crash.
- MIC: it can call the TIFF opener directly and bypass TIFF's normal guarded seek behavior, also producing a crash.
The reporter notes that TIFF's own seek path already checks both size and mode before reusing storage, which is why it served as a positive control rather than an affected format.
What was demonstrated
The advisory states the impact as process crash or denial of service, native out-of-bounds write, and a native over-read in the MPO path. The strongest claim is command execution in matching Python and Pillow runtime profiles through the MPO/JPEG path. The reporter tested Pillow 12.3.0 and says a harmless marker command succeeded in 10 of 10 runs against a local upload service that processes an unauthenticated MPO upload automatically, with a clean control in 10 of 10 runs. The reporter stresses that this used a fixed harmless marker only. The advisory also says the command-execution result depends on matching runtime profiles, so it should not be assumed to work on every deployment. Crashes are the more broadly applicable outcome.
Who is realistically exposed
The risk depends on whether untrusted files reach Pillow's multi-frame code. Typical exposed setups include:
- Upload endpoints that generate thumbnails, avatars or previews with Pillow.
- Background workers, CMS or e-commerce back ends, and document pipelines that open images from users or third parties.
- Services that iterate frames with ImageSequence.Iterator and call load() on each frame. The advisory shows this documented pattern reaching the MPO flaw.
- Apps built on libraries that depend on Pillow indirectly, such as Django ImageField validation or image-handling packages.
Applications that only open images you generate yourself are far less exposed. Pillow versions below 11.0.0 are outside the affected range stated in the advisory.
How to check your exposure
First find out which Pillow version each environment uses. The command below prints the installed version in the active Python environment; anything from 11.0.0 up to but not including 13.0.0 is affected.
python -m pip show PillowTo list every Pillow version pinned in a project, search your dependency files. Look for results below 13.0.0.
grep -rniE "^pillow|pillow==" requirements*.txt pyproject.toml Pipfile poetry.lock 2>/dev/nullRemember container images and CI caches: an old base image can carry a vulnerable Pillow even if your repository was updated.
What to do now
0 of 7 done
This command upgrades Pillow in the active environment and shows the resulting version, which should be 13.0.0 or higher.
python -m pip install --upgrade "Pillow>=13.0.0" && python -m pip show PillowDetection and monitoring
Memory corruption in a native library often shows up first as crashes. Review application and process-supervisor logs for worker restarts or segmentation faults around image uploads, and check upload logs for unusual PSD, MPO, DCX or MIC files, or JPEG-named files that are actually multi-frame. Because command execution was demonstrated, also look for unexpected child processes or outbound connections from the account that runs your image workers.
If a public site handles uploads, a free malware scan and the guidance on checking whether a website is hacked help you review the outside-in view after patching. If you suspect compromise, follow what to do when a website is hacked.
Timeline
| Date | Event |
|---|---|
| 16 Oct (release page) | Pillow 13.0.0 released with the fix |
| Oct 17, 2026 | GitHub advisory GHSA-v9pv-5gc8-w2m6 published |
| 2026-10-21 | CVE-2026-96639 published (CVE record) |
Sources
This article is based on the public sources below. Details can change as vendors and researchers publish more; check the original advisories before acting on version numbers.
