Jul 21, 2026·5 min read·28 visits
Unbounded zlib decompression in Pillow's PDF parser allows remote attackers to cause system Out-of-Memory crashes using crafted PDF files.
CVE-2026-59200 is a high-severity uncontrolled resource consumption vulnerability in the Pillow Python Imaging Library. The flaw resides in the PDF stream decoder, allowing remote, unauthenticated attackers to trigger host out-of-memory crashes by submitting malicious PDF decompression bombs.
Pillow is a widely adopted Python Imaging Library that handles graphic metadata parsing and image manipulation. Within its PDF handling routines, specifically inside the PDF stream decoder implemented in PdfParser.PdfStream.decode() in PIL/PdfParser.py, the library provides capability for decompressing binary vector data blocks.
The vulnerability is classified as an uncontrolled resource consumption flaw, mapping to CWE-400 and CWE-770, commonly referred to as a decompression bomb. Because decompression routines are processed synchronously upon parsing complex file formats, this flaw exposes a significant remote attack surface.
Any application utilizing Pillow to automatically process untrusted PDF uploads, generate thumbnails, or inspect image metadata is susceptible. The vulnerability does not require authentication or user interaction to trigger, making it an effective vector for targeting downstream processing daemons.
The underlying bug stems from an architectural misunderstanding of Python's standard zlib.decompress(data, bufsize) function. When the PDF stream parser encounters a /FlateDecode filter, it reads the /Length or /DL properties defined within the PDF dictionary wrapper to determine the expected decompressed stream length.
The developer assumed that specifying bufsize=int(expected_length) would act as a security limit, preventing Python's zlib wrapper from returning output that exceeds the specified length. In reality, Python's implementation uses the bufsize parameter solely as an initial memory buffer allocation hint to optimize execution, not as a maximum boundary limit.
When a zlib stream is parsed, if the actual uncompressed payload exceeds this initial size hint, the underlying C-native implementation automatically grows the allocation buffer dynamically. An attacker can construct a payload consisting of highly repetitive bytes (e.g., recursive null blocks) that compress highly efficiently but expand into gigabytes. This leads to unbounded memory allocation on the heap, triggering immediate host resource exhaustion and operating system kernel-level OOM (Out-Of-Memory) process termination.
The vulnerable code path inside PIL/PdfParser.py relied on direct execution of zlib.decompress using the untrusted length header:
# Vulnerable Implementation (< 12.3.0)
def decode(self) -> bytes:
try:
filter = self.dictionary[b"Filter"]
except KeyError:
return self.buf
if filter == b"FlateDecode":
try:
expected_length = self.dictionary[b"DL"]
except KeyError:
expected_length = self.dictionary[b"Length"]
# The bufsize parameter only acts as an allocation hint
return zlib.decompress(self.buf, bufsize=int(expected_length))The patched version replaces this logical block, introducing a secure streaming decompressor instantiation via zlib.decompressobj(). The patch implements validation using ImageFile.SAFEBLOCK as a hard limit:
# Patched Implementation (>= 12.3.0)
def decode(self, max_length: int = ImageFile.SAFEBLOCK) -> bytes:
try:
filter = self.dictionary[b"Filter"]
except KeyError:
return self.buf
if filter == b"FlateDecode":
# Use secure streaming object decompressor
dobj = zlib.decompressobj()
plaintext = dobj.decompress(self.buf, max_length)
# If there are unconsumed input bytes remaining, size exceeded the threshold
if dobj.unconsumed_tail:
msg = "Decompressed data too large"
raise ValueError(msg)
return plaintextBy leveraging dobj.unconsumed_tail, the parser validates if there is remaining compressed content that was not inflated because the max_length restriction was hit. If present, the routine immediately aborts, raising a ValueError prior to the allocation of additional system memory.
To exploit this vulnerability, an attacker constructs a PDF document containing a compressed stream object. The stream's content represents a high-expansion compression bomb utilizing standard DEFLATE compression.
The attacker structures the stream object with a /Length tag matched to the small compressed size to evade simple perimeter length checks. Once submitted to the target application, the backend parses the file and initiates decompression via the vulnerable PdfStream.decode() method.
Because the Python process executes this decompression step synchronously inside a native C module, the application is unable to interdict or limit the allocation once execution has been handed off to the standard library. The process continues to claim resources until terminated by the operating system kernel.
The impact of CVE-2026-59200 is restricted to availability, yielding a High score under the CVSS metric. Execution of the exploit results in an immediate Out-of-Memory event on the target host processing the file.
In standard enterprise environments, this triggers the OS kernel OOM-killer to terminate the application's daemon worker process (e.g., Gunicorn worker, Celery runner, or uWSGI process). This leads to localized Denial of Service for active connections and processing queues.
While this vulnerability does not yield remote code execution or data exposure, it represents a highly stable vector for disrupting service availability. Because the attack requires no privileges, any public-facing upload portal utilizing vulnerable parser modules is exposed to disruption.
The primary remediation path is upgrading the local Pillow dependency to version 12.3.0 or higher, which contains the official patch. This replaces the insecure decompress path with the streaming verification logic.
Where upgrading is not immediately feasible, you can apply workarounds at the application level. Ensure that applications restrict processed file types by explicitly defining safe formats within Image.open instead of utilizing default format detection.
# Safe application entry-point handling
SAFE_FORMATS = ["PNG", "JPEG", "WEBP"]
try:
im = Image.open(user_file, formats=SAFE_FORMATS)
im.verify()
except Exception as e:
raise ValueError("Unsupported format requested")Additionally, restrict operational resources at the operating system or containerization level. By establishing strict memory constraints on individual daemon processes, you can prevent a single payload execution from exhausting the entire virtual host resource pool.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network |
| CVSS | 7.5 |
| EPSS Score | 0.0035 |
| Impact | High (Denial of Service) |
| Exploit Status | Proof-of-Concept Available |
| KEV Status | Not Listed |
The software does not properly control the allocation of memory during decompression, enabling remote attackers to exhaust host RAM.
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.
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.
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.
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.
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.
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.