Jul 23, 2026·6 min read·4 visits
A flaw in the inline image parsing logic of pypdf versions prior to 6.14.1 allows an unauthenticated remote attacker to cause a complete Denial of Service (100% CPU thread utilization) by submitting a malformed PDF with a truncated inline image block.
An infinite loop vulnerability exists in the pure-python PDF library pypdf prior to version 6.14.1. This vulnerability occurs during the processing of malformed or truncated page content streams containing inline images. Specifically, the parser fails to validate the End-of-Stream condition during inline image dictionary parsing. This results in continuous backward stream-seeking and an infinite loop that consumes 100% CPU on the processing thread.
The vulnerability exists within the core content stream lexing engine of pypdf, specifically during the parsing of inline images in page content streams. In the PDF specification, inline images are embedded directly within content streams utilizing structural markers rather than being stored in external resource dictionaries. This parsing is executed by the internal content stream engine when functions like text extraction or rendering preparation are invoked.
Because PDF parsers are frequently deployed in automated pipeline environments (such as document ingestion systems, search indexing, and automated email processing), they present a significant unauthenticated attack surface. If an application utilizes a vulnerable version of pypdf to parse untrusted files, an attacker can exploit this surface to exhaust system resource limits.
This flaw is classified under CWE-400 (Uncontrolled Resource Consumption). It is caused by a failure to safely terminate a parsing loop when encountering an unexpected End-of-Stream (EOF) condition inside the inline image metadata blocks, leading directly to a Denial of Service (DoS).
To understand the root cause of CVE-2026-59936, we must analyze how PDF inline images are structured and how pypdf processes them. An inline image is bounded by three specific markers: BI (Begin Image), ID (Image Data), and EI (End Image). Between BI and ID, the PDF stream contains dictionary key-value settings defining properties such as width, height, and color space.
The parsing of these properties is performed by the _read_inline_image function in pypdf/generic/_data_structures.py. This function uses a while True loop to continuously fetch keys and values from the input stream using the helper function read_non_whitespace(stream). The parser reads the next token and then backs up the stream pointer by one byte using stream.seek(-1, 1) to evaluate the token's initial character.
If the stream ends prematurely—due to decompression errors, document truncation, or malicious formatting—read_non_whitespace(stream) returns an empty byte string (b""). Because the pre-patch code did not check whether the retrieved token was empty, the parser proceeded to execute the relative backward seek operation stream.seek(-1, 1). On the next loop iteration, the parser read from the same position, retrieved the empty token, backed up again, and repeated this sequence infinitely, locking the execution thread at maximum CPU utilization.
The vulnerability resides in pypdf/generic/_data_structures.py within the _read_inline_image method. Below is the vulnerable code block:
def _read_inline_image(self, stream: StreamType) -> dict[str, Any]:
settings = DictionaryObject()
while True:
tok = read_non_whitespace(stream)
# BUG: Missing verification of token presence.
# If tok is empty (EOF), seek moves the stream pointer backward.
stream.seek(-1, 1)
if tok == b"I":
# "ID" - begin of image data
...When read_non_whitespace(stream) encounters the end of the stream, it returns b"". The stream's current position pointer is at the very end of the file or stream block. The subsequent call to stream.seek(-1, 1) moves the stream pointer back by 1 byte. Consequently, subsequent read operations do not hit the natural EOF condition, preventing the stream object from reporting that it is exhausted. This establishes a self-sustaining loop that cannot terminate naturally.
The fix implemented in version 6.14.1 resolves this issue by inserting an explicit check for the empty token condition immediately after retrieving it from the helper function:
def _read_inline_image(self, stream: StreamType) -> dict[str, Any]:
settings = DictionaryObject()
while True:
tok = read_non_whitespace(stream)
# FIX: Validate that a token was successfully read.
# If empty, raise an explicit exception to exit the parsing cycle.
if not tok:
raise PdfStreamError("Unexpected end of stream.")
stream.seek(-1, 1)
if tok == b"I":
# "ID" - begin of image data
...By raising PdfStreamError, the parser aborts execution gracefully, preventing the infinite loop and allowing parent exception handlers to clean up resources.
Exploitation of CVE-2026-59936 is straightforward and does not require complex memory corruption payloads or bypasses for security mitigations like ASLR or DEP. An attacker only needs to generate a PDF document containing a malformed inline image sequence where the key-value settings block is truncated and lacks the matching ID and EI operators.
The minimal trigger payload consists of the start marker of an inline image block immediately followed by a key setting, with the stream abruptly ending. A representative sample is shown below:
BI
/IM true
/W001When a vulnerable application parses this content stream (for example, while invoking reader.pages[0].extract_text()), the parser enters the metadata parsing loop. It processes /IM true and /W001, and then encounters the end of the stream. Rather than terminating, the thread enters the infinite loop and utilizes 100% of its assigned CPU core. If the application handles file uploads asynchronously or sequentially in a single-threaded queue, a single malicious file can halt all subsequent document processing jobs.
The threat profile of this vulnerability is characterized as High. The authoritative CVSS v4.0 score is 8.7, reflecting a Network (N) attack vector with low complexity and zero required privileges or user interaction. The secondary CVSS v3.1 rating is 7.5, reflecting a High Availability impact.
In microservice architectures or cloud-native applications, worker processes are often shared across tenants. Exhausting CPU resources on a worker node can cause cascading failures, API timeouts, and increased operational costs due to automatic scaling engines attempting to provision additional nodes to handle the false load.
While there is currently no evidence of active exploitation in the wild (as reflected by its status in the CISA KEV and its low EPSS score of 0.00345), the simplicity of creating a functional proof-of-concept file elevates the risk of automated scanning or targeted denial-of-service attacks against public-facing document processing portals.
The primary remediation for this vulnerability is upgrading the pypdf dependency to version 6.14.1 or higher. This release contains the patch that correctly terminates execution by throwing a PdfStreamError when the end of stream is reached.
For environments where immediate upgrading is not possible, system administrators and developers should implement process-level isolation and timeout mechanisms. For instance, wrapping PDF parsing logic in worker tasks configured with hard execution limits (e.g., Celery task time limits or Python signal timers) ensures that hanging parser threads are forcibly terminated before exhausting system resources.
Additionally, applications should not perform PDF processing within the context of the main web server thread. Offloading these tasks to dedicated, sandboxed worker containers with restricted CPU allocations limits the blast radius of any successful exploit attempt.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
pypdf py-pdf | < 6.14.1 | 6.14.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network |
| CVSS v4.0 Score | 8.7 |
| EPSS Score | 0.00345 |
| Impact | Denial of Service (DoS) |
| Exploit Status | poc |
| KEV Status | Not Listed |
The software does not properly control the allocation and maintenance of a limited resource, enabling an attacker to influence the amount of resources consumed and eventually leading to a resource exhaustion state.
An uncontrolled memory allocation vulnerability (CWE-789) exists in pypdf prior to version 6.14.0. The library blindly trusted user-controlled image dimensions (/Width and /Height) from PDF metadata, allowing attackers to trigger physical memory exhaustion and an Out-of-Memory crash via tiny, malformed files.
An infinite loop vulnerability exists in the pypdf library prior to version 6.14.2 when processing malformed PDF inline images that utilize ASCII85 or ASCIIHex decoders. Attackers can exploit this vulnerability by submitting crafted PDF files containing incomplete streams, causing the parsing thread to consume 100% CPU resource indefinitely and leading to Denial of Service.
A critical CPU exhaustion vulnerability exists in the pypdf library before version 6.14.0. When parsing PDF files with malformed or corrupt standard cross-reference (xref) table entries, the library falls back to sequentially scanning the entire file buffer via regular expression search. An attacker can exploit this algorithmic bottleneck by supplying a crafted PDF with numerous malformed xref entries, leading to denial of service.
In Auth.js (specifically next-auth version 5), server-side configuration errors or backend exceptions can cause existence-based authentication and authorization checks to fail open. When the underlying core engine (@auth/core) encounters a configuration error or runtime exception, it returns an HTTP 500 Internal Server Error response containing a JSON-formatted error description payload. Prior to the fix, the next-auth wrappers read this response body using response.json() without validating the HTTP status code. Because any parsed JavaScript object evaluates to truthy, common existence-based checks incorrectly evaluated to true, letting unauthenticated requests pass.
A host/authority desynchronization vulnerability exists in Eclipse Jetty's handling of HTTP/2 and HTTP/3 requests. When both an ':authority' pseudo-header and a standard 'Host' header are present in a request, Jetty fails to validate that they represent the same target host. This desynchronization creates a host-confusion or 'split-brain' state, enabling attackers to bypass access control lists, escape virtual host isolation, poison web caches, or manipulate redirect targets in backend applications.
A path traversal vulnerability exists in Eclipse Jetty due to a state-desynchronization defect in the URI parsing state machine inside URIUtil.java. This defect allows unauthenticated remote attackers to bypass path-based security constraints enforced by downstream filters, application gateways, or authorization modules by crafting URIs with path parameter delimiters and parent directory traversal sequences.