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

CVE-2026-96637 — pillow

Fix: python-pillow/Pillow@7cb0e02

CVE-2026-96637 is a Uncontrolled Resource Consumption vulnerability in pillow. A fix is available for pillow — see the affected versions and patch details below.

Pillow: Decompression bomb check bypass in SpiderImagePlugin.seek() leads to unbounded allocation DoS

Also known asGHSA-g842-9rc4-69pw
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

SpiderImagePlugin's seek() bypasses the decompression bomb check, allowing a tiny (<1KB) crafted SPIDER stack file to trigger a multi-gigabyte allocation on im.load().

Affected versions

All versions up to and including current HEAD (v13.0.0.dev0, commit e7a7ea84)

Root cause

Image.open() runs Image._decompression_bomb_check(im.size) exactly once, in _open_core (src/PIL/Image.py:3668), right after the initial _open().

SPIDER stack files are multi-frame: SpiderImageFile.seek(frame) (SpiderImagePlugin.py:179-190) computes an attacker-influenced stkoffset and calls self._open() AGAIN directly (line 190) — NOT through Image.open(). _open() re-parses a fresh 27-float header at that offset and reassigns:

self._size = int(h[12]), int(h[2])      # SpiderImagePlugin.py:131

straight from unvalidated header floats (isSpiderHeader only checks internal labrec*lenbyt==labbyt consistency, never bounds nsam/nrow). No bomb check runs on this new size.

On the subsequent load(), ImageFile.load_prepare() (ImageFile.py:436-437) executes self.im = Image.core.new(self.mode, self.size) with the unchecked, attacker-chosen size. Mode is always "F" (4 bytes/pixel), so e.g. 40000x40000 = 6.4 GB.

Why this is a real bug

Sibling multi-frame plugins DO re-check on their alternate paths:

  • GifImagePlugin.py:286 - Image._decompression_bomb_check(self._size) (in seek)
  • TiffImagePlugin.py:1309 - Image._decompression_bomb_check(self._tile_size)
  • IcnsImagePlugin.py:121/138, IcoImagePlugin.py:236/240 (sub-image selection)

SpiderImagePlugin has NO bomb check anywhere. This is the same class of bug just fixed for GdImageFile (GHSA-phj9-mv4w-65pm) and the FontFile classes (GHSA-5x94/8v84/45hq): an allocation reached on a non-Image.open() path sized by an attacker header field, independent of the one-time pixel bomb check.

Reproduction

Python 3.13, Pillow HEAD e7a7ea84

Attached: pillow_poc_spider_bomb.py

Sets Image.MAX_IMAGE_PIXELS=5000, then:

  • CONTROL: Image.open(standalone 100x100 SPIDER) -> DecompressionBombWarning fires correctly.
  • BYPASS: Image.open(stack, frame0=1x1) passes; im.seek(1) sets size (100,100) with NO DecompressionBombWarning/Error -> the check is bypassed.

Real-world impact: a large frame-1 nsam/nrow (e.g. 40000x40000) causes im.load() to allocate ~6.4 GB (mode F) with the bomb check fully bypassed, from a file under 1 KB.

Suggested fix

Call Image._decompression_bomb_check(self.size) at the end of SpiderImageFile._open() (or in seek() after _open()), mirroring GifImagePlugin / TiffImagePlugin. Optionally validate nsam/nrow are positive and sane in _open().

Credit: Yazan Balawneh - CyStack Security Team pillow_poc_spider_bomb.py

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-96637 is resolved across your whole dependency graph.

  3. Workarounds

    Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.

Frequently Asked Questions

## Summary SpiderImagePlugin's seek() bypasses the decompression bomb check, allowing a tiny (<1KB) crafted SPIDER stack file to trigger a multi-gigabyte allocation on im.load(). ## Affected versions All versions up to and including current HEAD (v13.0.0.dev0, commit e7a7ea84) ## Root cause Image.open() runs Image._decompression_bomb_check(im.size) exactly once, in _open_core (src/PIL/Image.py:3668), right after the initial _open(). SPIDER stack files are multi-frame: SpiderImageFile.seek(frame) (SpiderImagePlugin.py:179-190) computes an attacker-influenced stkoffset and calls self._open()
O3 Security · Impact-Aware SCA

Is CVE-2026-96637 in your dependencies?

Find it across PyPI, including transitive dependencies.

CVE-2026-96637: pillow — Fixed in 13.0.0