CVE-2026-96636: Pillow: Decompression-bomb protection bypass via multi-frame seek() in MPO and DCX (MpoImageFile.seek / DcxImageFile.seek)

Published Oct 21, 2026
·
Updated

Summary Pillow's decompression-bomb protection (Image.decompressionbombcheck, gated by Image.MAXIMAGEPIXELS) is enforced only on the first frame, inside Image.open(). For multi-frame formats, seek() re-parses the target frame's header and resets self.size. GifImagePlugin and TiffImagePlugin re-run the bomb check on seek(), but MpoImageFile.seek() and DcxImageFile.seek() do not. A crafted MPO or DCX whose first frame is tiny (passing the check) but whose later frame declares enormous dimensions therefore bypasses the protection: loading that frame decodes an over-limit image and commits large amounts of memory — a denial of service (memory exhaustion / OOM).

Same class as the recently assigned CVE-2026-55379 (BdfFontFile), CVE-2026-55380 (GdImageFile) and CVE-2026-40192 (FITS GZIP).

Details Image.open() → opencore() runs decompressionbombcheck(im.size) once, on the frame present at open time. Both vulnerable seek() methods re-parse a new frame and set a new self.size without re-checking:

- PIL/MpoImagePlugin.py, MpoImageFile.seek(): calls JpegImagePlugin.JpegImageFile.open(self) (which sets self.size from the frame's JPEG SOF) and rebuilds self.tile, with no Image.decompressionbombcheck(self.size). - PIL/DcxImagePlugin.py, DcxImageFile.seek(): calls PcxImageFile.open(self) (which sets self.size from the page's PCX header), with no check.

Confirmed present in 12.3.0 (latest release) and on main (13.0.0.dev0). Compare GifImagePlugin.seek and TiffImagePlugin, which call decompressionbombcheck correctly on seek.

PoC Self-contained; the victim uses default MAXIMAGEPIXELS:

python import io from PIL import Image

attacker builds the file (guard disabled only to CREATE the oversized frame) Image.MAXIMAGEPIXELS = None small = Image.new("RGB", (8, 8)) big = Image.new("RGB", (14000, 14000), (120, 60, 30)) # 196,000,000 px > error threshold buf = io.BytesIO() small.save(buf, format="MPO", saveall=True, appendimages=[big]) data = buf.getvalue() # ~3 MB Image.MAXIMAGEPIXELS = int(1024 1024 1024 // 4 // 3) # restore default

victim (default limits) im = Image.open(io.BytesIO(data)) # frame 0 (8x8) passes the bomb check im.seek(1) # frame 1 (14000x14000) -> NOT re-checked <-- bug im.load() # decodes the over-limit image, commits memory print("loaded over-limit frame:", im.size)

Measured on Pillow 12.3.0 (default limit 89,478,485 px; error threshold 178,956,970 px):

| Vector | File size | Frame 1 | Peak RSS | |---|---|---|---| | MPO (JPEG) | ~3.0 MB | 14000×14000 (196 Mpx) | ~763 MB | | DCX (PCX) | ~6.2 MB | 14000×14000 (196 Mpx) | ~200 MB | | Control: same image standalone | — | 196 Mpx | DecompressionBombError (correctly blocked) |

The DCX vector (0x3ADE68B1 magic + offset directory + two PCX pages) behaves identically via DcxImageFile.seek(). Scaling frame 1 toward JPEG's ~65500×65500 maximum drives multi-GB commitments from a small file, reliably OOM-killing the process.

Impact Denial of service (memory exhaustion / OOM) — CWE-400 / CWE-1284, a bypass of Pillow's documented decompression-bomb mitigation. seek() + load() is the normal way to iterate frames (e.g. PIL.ImageSequence.Iterator, thumbnailers, converters, 3D-photo handlers). Because MPO is auto-detected from a JPEG carrying MPF metadata, a file served with a .jpg extension can be opened as MPO, so any application that iterates frames of untrusted images is exposed.

Suggested fix Re-run the check after seek() re-parses a frame, mirroring GIF/TIFF:

python PIL/MpoImagePlugin.py — end of MpoImageFile.seek() self.tile = [ImageFile.Tile("jpeg", (0, 0) + self.size, self.offset, self.tile[0][-1])] Image.decompressionbombcheck(self.size) # add self.frame = frame

PIL/DcxImagePlugin.py — end of DcxImageFile.seek() PcxImageFile.open(self) Image.decompressionbombcheck(self.size) # add

Other sources

Pillow is a Python imaging library. Prior to 13.0.0, MpoImageFile.seek() and DcxImageFile.seek() reparse later MPO or DCX frames and replace self.size without rerunning Image.decompressionbombcheck(). A crafted multi-frame image can use a tiny first frame to pass Image.open() and then declare an over-limit later frame. Normal frame iteration through seek(), load(), or ImageSequence.Iterator can decode that frame and commit hundreds of megabytes or more, causing memory-exhaustion denial of service. This issue is fixed in version 13.0.0.

— MITRE

Affected Software

19 affected componentsFixes available
pypi/Pillow<13.0.0
pip/pillow<13.0.0
13.0.0
bitnami/pillow<13.0.0
13.0.0
pip/pillow<13.0.0
13.0.0
Python Pillow<13.0.0
debian/pillow*
debian/pillow*
debian/pillow*
debian/pillow<=9.4.0-1.1+deb12u1, <=11.1.0-5+deb13u4, <=11.1.0-5+deb13u3, <=12.3.0-1
13.0.0-1
ubuntu/pillow*
ubuntu/pillow*
ubuntu/pillow*
ubuntu/pillow-python2*
ubuntu/pillow*
ubuntu/pillow*
ubuntu/pillow*
ubuntu/pillow*
IBM App Connect Operator<=CD: 12.1.0 - 12.21.0 12.0 LTS: 12.0.0 - 12.0.28 13 LTS: 13.0.0 - 13.6.0
IBM App Connect EnterpriseCertified Containers Operands<=CD: 12.0.11.1-r1 - 12.0.12.5-r1, 13.0.1.0-r1 - 13.0.6.2-r1 12.0 LTS: 12.0.12-r1 - 12.0.12-r28 13 LTS: 13.0.7.0-r1 - 13.0.9.0-r1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/Pillow to a version that resolves this vulnerability.

    Fixed in 13.0.0
  2. Upgrade

    Upgrade bitnami/pillow to a version that resolves this vulnerability.

    Fixed in 13.0.0
  3. Upgrade

    Upgrade pip/pillow to a version that resolves this vulnerability.

    Fixed in 13.0.0
  4. Upgrade

    Upgrade debian/pillow to a version that resolves this vulnerability.

    Fixed in 13.0.0-1
  5. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 13.0.0

Event History

Oct 21, 2026
CVE Published
via MITRE·09:05 PM
Data Sourced
via MITRE·09:05 PM
DescriptionSeverityWeakness
Advisory Published
via OSV·09:20 PM
Data Sourced
via NVD·09:20 PM
RemedyDescriptionSeverityWeaknessAffected Software
Oct 22, 2026
Advisory Published
via GitHub·03:56 PM
Data Sourced
via GitHub·03:56 PM
DescriptionSeverityWeaknessAffected Software
Nov 12, 2026
Data Sourced
via OSV·11:49 PM
Affected Software

Parent advisories

This vulnerability appears in the following advisories.

Frequently Asked Questions

1

Which applications are exposed to this issue?

Applications using Pillow before 13.0.0 are exposed if they open attacker-controlled MPO or DCX files and subsequently access later frames, including through seek(), load(), or ImageSequence.Iterator. Processing only the initial frame does not describe the vulnerable path.

2

What must an attacker provide to trigger the denial of service?

An attacker needs to supply a crafted multi-frame MPO or DCX image with a small initial frame and a later frame that declares dimensions beyond Pillow's decompression-bomb limit. Exploitation requires the application to process that later frame; no attacker privileges are required, but user interaction is indicated by the supplied severity vector.

3

What should be done if upgrading is not immediately possible?

Avoid processing untrusted MPO and DCX files beyond their first frame. In particular, do not iterate frames or call seek() on untrusted files until Pillow can be updated to version 13.0.0.

4

How can I determine whether my application is affected?

Check whether it uses Pillow versions earlier than 13.0.0 and accepts or processes MPO or DCX images from untrusted sources. Applications that iterate image frames, use ImageSequence.Iterator, or call seek() and load() on these formats are affected by the described bypass.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203