---
id: "CVE-2026-96637"
kind: "vulnerability"
aliases: ["CVE-2026-96637", "GHSA-g842-9rc4-69pw"]
title: "Pillow: Decompression bomb check bypass in SpiderImagePlugin.seek() leads to unbounded allocation DoS"
severity: "high"
cvss: 7.1
epss: 0.003329999977722764
exploited: false
status: "open"
category: "package"
products: [{"name": "pillow", "fixed": ["13.0.0"], "affected": "< 13.0.0", "ecosystem": "pypi"}]
ecosystems: ["pypi"]
tags: ["cwe-400", "cwe-409", "image-upload", "package", "pillow", "python", "self-hosted"]
not_affected_if: ["no-multi-frame-seek", "no-untrusted-spider-input", "pillow-at-or-above-13.0.0"]
published_at: "2026-10-21T21:20:31+00:00"
first_seen_at: "2026-10-23T19:11:32+00:00"
updated_at: "2026-10-23T21:57:29+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-g842-9rc4-69pw", "first_seen_at": "2026-10-23T19:11:32+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-96637", "first_seen_at": "2026-10-23T21:57:29+00:00"}]
---

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

Pillow's SPIDER image plugin skips the decompression bomb size check when seeking to later frames, so a tiny crafted SPIDER stack file can make im.load() allocate multiple gigabytes. An attacker who gets a malicious image processed by an app using Pillow can exhaust memory and cause denial of service.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- pypi:pillow affected < 13.0.0 fixed 13.0.0

## How to check

Run pip show pillow and check whether the version is below 13.0.0.
Find Pillow in requirements or lockfiles with grep -i pillow requirements*.txt poetry.lock uv.lock Pipfile.lock.
Check whether the app opens untrusted SPIDER (.spi, .spider) files with Image.open() and then seeks frames.

## Fix

Upgrade Pillow to 13.0.0 or later (pip install -U 'pillow>=13.0.0').
Until then, reject or do not process SPIDER uploads from untrusted users.

## Timeline

- 2026-10-23T19:11:32+00:00 ghsa https://github.com/advisories/GHSA-g842-9rc4-69pw
- 2026-10-23T21:57:29+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-96637

## From the sources

## 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](https://github.com/user-attachments/files/30244916/pillow_poc_spider_bomb.py)

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
