CVE-2026-96637High· 6.5▾ TwilightPillow: Decompression bomb check bypass in SpiderImagePlugin.seek() leads to unbounded allocation DoS
▾ Twilight zone — High severity, or a signal on a lesser flaw
impact 35.8 · likelihood 0.1 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Stakeholder-Specific Vulnerability Categorization from CISA's ADP record at CVE.org: whether exploitation is observed, whether an attack can be automated, and how much of the system is at stake. The CVSS score shown above comes from the CISA ADP record, not NVD.
Exploit-prediction probability, daily snapshots since Oct 23.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via OSV
0.3%
0.3% → 0.4%
7.1 → 6.5
Last analysed / modified upstream
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().
All versions up to and including current HEAD (v13.0.0.dev0, commit e7a7ea84)
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.
Sibling multi-frame plugins DO re-check on their alternate paths:
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.
Python 3.13, Pillow HEAD e7a7ea84
Attached: pillow_poc_spider_bomb.py
Sets Image.MAX_IMAGE_PIXELS=5000, then:
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.
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
pillow < 13.0.0Upgrade to a patched release:
pillow 13.0.0Field changes observed since this record was first indexed.
Connected by shared product, vendor, weakness, or advisory.
CVE-2026-96636Medium· 6.5Pillow: Decompression-bomb protection bypass via multi-frame seek() in MPO and DCX (MpoImageFile.seek / DcxImageFile.seek)
CVE-2026-96640High· 8.2Pillow PDF append accepts malformed shared page-tree DAG causing resource exhaustion
CVE-2026-96638Medium· 6.5Pillow: BMP RLE decoder memory-amplification DoS via unbounded delta escape / absolute mode
CVE-2026-112400High· 7.5Pillow: GIF seek keeps rejected size after DecompressionBombError, allowing DoS
CVE-2026-40192High· 7.5Pillow is a Python imaging library
CVE-2026-96639Critical· 9.8Pillow multi-frame loaders can reuse stale native image storage after mode transitions