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.

Print the installed Pillow version (run inside each virtualenv or container)Shell
python -m pip show Pillow

To list every Pillow version pinned in a project, search your dependency files. Look for results below 13.0.0.

Find Pillow pins in dependency files (run from the project root)Shell
grep -rniE "^pillow|pillow==" requirements*.txt pyproject.toml Pipfile poetry.lock 2>/dev/null

Remember container images and CI caches: an old base image can carry a vulnerable Pillow even if your repository was updated.

What to do now

This command upgrades Pillow in the active environment and shows the resulting version, which should be 13.0.0 or higher.

Upgrade Pillow and confirm the version (run in each Python environment)Shell
python -m pip install --upgrade "Pillow>=13.0.0" && python -m pip show Pillow
Caution. Upgrading a major version can change behavior. The 13.0.0 release notes list removals and deprecations, including dropping Python 3.10. Test in staging first, and restart the app servers and workers after the upgrade.
Tip. If you cannot upgrade immediately, you can restrict which formats Pillow will try to open by passing the formats argument to Image.open with an allow-list such as JPEG and PNG. This is a general hardening technique, not a fix described in the advisory, so treat it as a stopgap and still upgrade.

Detection 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

Summary
DateEvent
16 Oct (release page)Pillow 13.0.0 released with the fix
Oct 17, 2026GitHub advisory GHSA-v9pv-5gc8-w2m6 published
2026-10-21CVE-2026-96639 published (CVE record)
As of this writing, the research contains no report of in-the-wild exploitation and the CVE is not in CISA's KEV catalog. The reporter says private proof-of-concept artifacts were offered to maintainers only. Status may change, so patch rather than wait.

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.

  1. CVE-2026-96639 - GitHub Advisory Databasegithub.com
  2. Clear core image if mode or size has changed during seek() (#10105) · python-pillow/Pillow@78d97d9github.com
  3. Release 13.0.0 · python-pillow/Pillowgithub.com
  • Pillow
  • Python
  • CVE-2026-96639
  • image processing
  • file upload