CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-59204

CVE-2026-59204: Denial of Service via Memory Exhaustion in Pillow JPEG2000 Decoder

Amit Schendel
Amit Schendel
Senior Security Researcher

Jul 21, 2026·7 min read·32 visits

Executive Summary (TL;DR)

A state accumulation bug in Pillow's JPEG2000 tile decoder allows unauthenticated attackers to crash applications via Out-of-Memory termination using crafted multi-tiled images.

A Denial of Service vulnerability exists in the JPEG2000 decoder of Pillow (versions 8.2.0 to 12.2.0) due to memory allocation state accumulation across tiles, leading to rapid process termination.

Vulnerability Overview

The Pillow imaging library includes native C implementations for processing specialized image formats, including the JPEG2000 standard. The JPEG2000 decoder relies on the open-source OpenJPEG library to handle the compressed codestream, passing decoded pixels to a Pillow-specific memory buffer for structural reorganization. Within this subsystem, a security flaw was discovered in the tiled decompressor logic that permits unauthenticated remote attackers to trigger resource exhaustion.

The vulnerability is classified under CWE-789 (Memory Allocation with Excessive Size Value) and CWE-770 (Allocation of Resources Without Limits or Throttling). It exists because the decoder retains and accumulates state variables across independent decoding operations. Consequently, processing multi-tiled JPEG2000 images leads to exponential memory overhead relative to the dimensions of the file.

This technical analysis details the memory-management flaw within Pillow's decoding routine, walks through the C code patch, and outlines defensive mechanisms to prevent process crash attacks. The flaw affects Pillow versions from 8.2.0 through 12.2.0 and has been resolved in version 12.3.0.

Root Cause Analysis

To understand the mechanics of the vulnerability, it is necessary to examine how the JPEG2000 standard utilizes image tiling. Tiling divides an image into discrete, independent rectangular segments that can be decoded sequentially, minimizing the overall memory requirements for systems processing large images. Pillow handles this logic within the j2k_decode_entry function inside the src/libImaging/Jpeg2KDecode.c source file.

During the decoding sequence, the function loops through each individual tile, extracting metadata and allocating a transient scratch buffer to store the raw decompressed pixel bytes. To size this scratch buffer, the engine calculates the required byte allocation using the dimensions of the active tile and the width of its cumulative component channels. This width tracking is managed by the integer variable total_component_width.

In vulnerable versions, the decoder initializes total_component_width to zero outside the loop that iterates over each image tile. As the decoder moves from one tile to the next, it fails to reset this variable. Instead, the decoder continually adds the component widths of subsequent tiles to the running total.

When the decoder invokes realloc on the state scratch buffer to accommodate the newly computed dimensions, the memory requirement balloons far beyond the system's actual capacity. For images configured with hundreds or thousands of tiny tiles, this accumulation behavior rapidly scales the allocation request into gigabytes. The operating system encounters an Out-of-Memory (OOM) condition and terminates the parent Python process, resulting in a denial of service.

Code Analysis & Memory Allocation Flow

The vulnerability resides in src/libImaging/Jpeg2KDecode.c. Below is an annotated visual comparison of the vulnerable decoding structure versus the remediated code path.

/* VULNERABLE CODE - src/libImaging/Jpeg2KDecode.c */
j2k_decode_entry(Imaging im, ImagingCodecState state) {
    size_t tile_bytes = 0;
    unsigned n, tile_height, tile_width;
    int total_component_width = 0; // Vulnerability: Declared outside the loop scope
 
    stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
 
    // Loop iterating over each tile in the JPEG2000 codestream
    for (/* tile loop */) {
        // total_component_width is NOT reset to 0 here
 
        for (n = 0; n < tile_info.nb_comps; n++) {
            int csize = (image->comps[n].prec + 7) >> 3;
            // Accumulation bug: Adds to the value from previous iterations
            total_component_width += csize * image->comps[n].w;
        }
 
        // Formula generates progressively larger sizes
        tile_bytes = total_component_width * tile_height * tile_width;
 
        // Allocation size exceeds physical memory bounds
        state->buffer = realloc(state->buffer, tile_bytes);
    }
}

The patch merged in Git commit 13ada41172142f2fd9f0906f615a00ea623a11ca resolves this state accumulation by shifting the declaration of total_component_width to local scope within the loop.

/* PATCHED CODE - src/libImaging/Jpeg2KDecode.c */
j2k_decode_entry(Imaging im, ImagingCodecState state) {
    size_t tile_bytes = 0;
    unsigned n, tile_height, tile_width;
    // total_component_width removed from this outer scope
 
    stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
 
    for (/* tile loop */) {
        // Fix: re-initialized to 0 on every loop iteration
        int total_component_width = 0;
 
        for (n = 0; n < tile_info.nb_comps; n++) {
            int csize = (image->comps[n].prec + 7) >> 3;
            // Accumulation is restricted to the components of the current tile
            total_component_width += csize * image->comps[n].w;
        }
 
        // Calculation accurately reflects only the active tile
        tile_bytes = total_component_width * tile_height * tile_width;
 
        // realloc scales within correct boundaries
        state->buffer = realloc(state->buffer, tile_bytes);
    }
}

While this patch effectively isolates tile calculations, security engineers must note that state->buffer is resized upward via realloc() but is never resized downward. If an image begins with an extremely large tile followed by numerous small tiles, the peak memory allocation remains allocated on the heap for the duration of the decoding cycle. This behavior can still create a high resident memory footprint under specific conditions.

Exploitation & Attack Methodology

Exploiting CVE-2026-59204 requires no specific privileges and can be executed remotely if the target application processes user-supplied images. The attacker must supply a JPEG2000 file crafted with a dense grid of tiles. Because standard defensive controls in web applications typically inspect total image dimensions rather than tile density, the payload file easily bypasses standard image-resolution filters.

The attacker constructs a JPEG2000 image codestream containing a high count of small tiles (for example, a 2000x2000 pixel image divided into 10x10 pixel tiles, yielding 40,000 distinct tiles). The payload is transmitted via an image upload or manipulation endpoint. When the backend service loads the image data, Pillow's format sniffer recognizes the JPEG2000 magic bytes and routes the data stream to the decoding logic.

As the decoder processes the 40,000 tiles sequentially, the variable total_component_width increases during every iteration. By the time the loop reaches intermediate iterations, the calculated value of tile_bytes requests several gigabytes of memory. Because the system cannot satisfy this sudden request for contiguous memory, the runtime environment throws an out-of-memory error, causing the operating system to immediately terminate the worker process.

Impact Assessment & Threat Metrics

The impact of CVE-2026-59204 is restricted to application-level Denial of Service (DoS). Because the vulnerability results in process termination due to unhandled out-of-memory conditions, there is no direct risk of remote code execution or unauthorized information disclosure. However, in modern microservice architectures, a persistent crash of image processing nodes can degrade overall service availability.

The Common Vulnerability Scoring System (CVSS) v3.1 assigns a Base Score of 7.5 (High) to this flaw, reflecting network-based exploitability with low complexity and zero privilege requirements. Under CVSS v4.0, the severity is rated at 8.7 (High), emphasizing the total loss of availability for the parsing service.

The Exploit Prediction Scoring System (EPSS) maintains a low immediate probability of active exploitation in wild campaigns. The vulnerability is not currently listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and there are no public weaponized exploits available.

Detection & Technical Mitigations

Organizations running vulnerable Pillow instances should upgrade to version 12.3.0 or later to patch the underlying C source files. When immediate library upgrades are not feasible, security administrators can implement input filtering at the application boundary to reject JPEG2000 images before they reach the decoding subsystem.

The most effective application-level workaround is to restrict the permitted image formats during the invocation of Image.open(). By explicitly specifying an allowed format list, the Pillow engine will reject JPEG2000 codestreams without attempting to parse them.

from PIL import Image
 
# Enforce an explicit format whitelist excluding JPEG2000
SAFE_FORMATS = ["PNG", "JPEG", "GIF", "WEBP"]
 
try:
    with Image.open("user_file.bin", formats=SAFE_FORMATS) as img:
        img.load()
except Exception as err:
    # Handle rejected format safely
    pass

To mitigate the system-level impact of potential memory exhaustion, deploy image-processing workers inside isolated execution environments with strict resource constraints. For example, Docker containers can be configured with memory ceilings to prevent a single parsing process from consuming host resources or affecting neighboring services. This ensures that process termination affects only the processing container and does not cause host-level cascading failures.

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.40%
Top 68% most exploited

Affected Systems

python-pillow/Pillow

Affected Versions Detail

Product
Affected Versions
Fixed Version
Pillow
python-pillow
>= 8.2.0, < 12.3.012.3.0
AttributeDetail
CWE IDCWE-789 / CWE-770
Attack VectorNetwork (Unauthenticated)
CVSS Score7.5 (v3.1) / 8.7 (v4.0)
EPSS Score0.00398 (32.21% Percentile)
ImpactDenial of Service (OOM Crash)
Exploit StatusNone / No Public Exploits
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499.004Endpoint Denial of Service: Application Exhaustion
Impact
CWE-789
Memory Allocation with Excessive Size Value

The product allocates memory based on an untrusted size value, but it does not validate or limit the size of the request, leading to potential resource exhaustion.

Vulnerability Timeline

Fix commit merged in pull request #9704
2026-06-22
Security Advisory published and CVE-2026-59204 assigned
2026-07-14

References & Sources

  • [1]GitHub Security Advisory GHSA-vjc4-5qp5-m44j
  • [2]NVD CVE-2026-59204 Detail
  • [3]Official Security Fix Commit
  • [4]Vulnerability Fix Pull Request (#9704)
  • [5]Pillow 12.3.0 Release Notes
  • [6]CVE.org Record

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•39 minutes ago•CVE-2026-53653
8.7

CVE-2026-53653: Unauthenticated Denial of Service via Unbounded Image Derivative Dimensions in Grav CMS

Grav CMS prior to version 1.7.53 and 2.0.0-rc.8 is vulnerable to an unauthenticated remote denial of service (DoS) vulnerability. By supplying crafted query parameters with extremely large dimensions to image assets, remote unauthenticated attackers can force the server to allocate massive amounts of system memory, leading to kernel Out-Of-Memory (OOM) termination of web worker processes.

Amit Schendel
Amit Schendel
2 views•7 min read
•about 2 hours ago•CVE-2026-53657
8.2

CVE-2026-53657: Privilege Escalation via Overly Permissive Unix Domain Socket in Lima Guest Agent

CVE-2026-53657 is a local privilege escalation vulnerability in Lima (lima-vm/lima) affecting versions prior to 2.1.3 when configured with the QEMU driver. The guest agent daemon, running as root, creates its communication socket `/run/lima-guestagent.sock` with world-writable permissions (0777). This allows unprivileged local users to command the agent to establish arbitrary tunnels, including to privileged local UNIX sockets (like D-Bus). Because the target daemon authenticates the incoming connection using the credentials of the root-owned guest agent (via SO_PEERCRED), unprivileged users can perform root operations, resulting in complete guest VM compromise.

Amit Schendel
Amit Schendel
2 views•9 min read
•about 3 hours ago•CVE-2026-10740
5.3

CVE-2026-10740: Denial of Service in s2n-quic CryptoStream Reassembly

An unauthenticated Denial of Service vulnerability exists in the s2n-quic library's CryptoStream reassembler due to a lack of buffer limits on out-of-order cryptographic frames. An attacker can transmit a crafted CRYPTO frame with an extremely high offset and nominal payload, forcing the receiver to execute unbounded memory allocations and causing service crashes.

Amit Schendel
Amit Schendel
3 views•7 min read
•about 4 hours ago•CVE-2026-55153
7.1

CVE-2026-55153: JNDI Injection and Deserialization Gadget Abuse in mchange-commons-java

A JNDI Injection and Deserialization Gadget vulnerability exists in mchange-commons-java prior to version 0.6.0. The com.mchange.v2.naming.JavaBeanObjectFactory component permits arbitrary class instantiation and setter invocation, allowing attackers to perform Server-Side Request Forgery (SSRF) and remote class loading.

Alon Barad
Alon Barad
6 views•5 min read
•about 5 hours ago•GHSA-8RW6-P7M8-63JP
6.5

GHSA-8RW6-P7M8-63JP: Array Element-Level SELECT Permissions Leak in SurrealDB

SurrealDB versions supporting element-level SELECT permissions on arrays are vulnerable to a logical authorization bypass. Due to an index-shifting error during array filtration, restricted elements can skip permission checks and leak to unauthorized record users.

Alon Barad
Alon Barad
5 views•6 min read
•1 day ago•CVE-2026-12243
7.5

CVE-2026-12243: Incomplete Path Traversal Validation and Percent-Encoding Bypass in NLTK

CVE-2026-12243 is a path traversal vulnerability in the Natural Language Toolkit (NLTK) version 3.9.4. The flaw exists because the input validation routine fails to account for percent-encoded directory traversal sequences like '..%2f' before passing them to urllib.request.url2pathname(), which decodes them into active traversal sequences.

Amit Schendel
Amit Schendel
5 views•8 min read