Sep 2, 2026·6 min read·66 visits
Infinite loop (CWE-835) in pypdf < 6.16.0 allows unauthenticated attackers to cause complete CPU exhaustion (DoS) via crafted PDF structures containing cyclic tree relationships.
An infinite loop vulnerability in pypdf versions prior to 6.16.0 allows attackers to trigger computational resource exhaustion and complete thread locking by supplying a malformed PDF with a cyclic tree structure. When modifying or rewriting document outlines containing circular references, the library endlessly traverses /Next pointers, resulting in application denial of service.
The vulnerability resides in pypdf (formerly PyPDF2), a popular and widely integrated pure-Python library designed for PDF file parsing, generation, and structure modification. The specific component affected is the TreeObject class, implemented within pypdf/generic/_data_structures.py. This class processes hierarchical elements such as the document's outline bookmark systems and internal page tree directories.
Many web-based software ecosystems and microservices utilize pypdf to accept document uploads from untrusted clients, perform automated updates, merge PDF pages, or modify structural attributes before saving or rendering the output. These processing operations expose a technical attack surface by executing code paths that traverse, validate, or update hierarchical tree elements in the target PDF document.
By supplying a crafted PDF stream containing a recursive, loop-configured tree structure, an attacker can manipulate execution flow. When a vulnerable writing mechanism (such as TreeObject.insert_child) interacts with the tree, it enters an infinite loop. This vulnerability is classified under CWE-835: Loop with Unreachable Exit Condition, and results in application-level denial of service.
In standard PDF files, hierarchical node systems like document outlines are stored as structured dictionary objects linked via structural parameters. The standard dictates the use of designated keys, specifically /First, /Last, /Next, /Prev, and /Parent, to map sibling and parental relationships. Software libraries rely on these parameters to walk linear paths through the document tree structures.
When pypdf performs actions like adding structural markers or reassembling metadata layers, it invokes the TreeObject.insert_child method. The function is designed to iterate sequentially starting from the terminal node /Last (assigned to variable prev) and traverse forward by repeatedly evaluating the /Next key relationships of successive elements.
In vulnerable versions of the library, the traversal loop compares the target traversal node's reference to a tracking marker variable called before. The loop terminates only when this exact target is matched, or when a node is parsed that completely lacks a /Next pointer. An attacker can break this termination assumption by defining circular sibling nodes (e.g., Sibling A /Next -> Sibling B /Next -> Sibling A). Under this condition, the traversal path forms a closed ring, bypassing the exit criteria and resulting in a thread-blocking loop.
An analysis of the patch shows the modification introduced in the file pypdf/generic/_data_structures.py to fix this loop. Below is a comparative illustration of the code before and after the application of the official security patch.
# Vulnerable implementation in pypdf < 6.16.0
prev = cast("DictionaryObject", self["/Last"])
while prev.indirect_reference != before:
if "/Next" in prev:
prev = cast("TreeObject", prev["/Next"])
else: # append at the end
prev[NameObject("/Next")] = cast("TreeObject", child_reference)
# ... [omitted logic to update child relationships] ...
return child_referenceIn the vulnerable implementation, prev is assigned dynamically, but the iteration does not record which memory structures have already been parsed. The loop runs blindly across identical references, consuming processing memory and thread execution indefinitely.
# Patched implementation in pypdf >= 6.16.0 (Commit c9ba557d565d57c53a0b3a0be06c0a4c29b0559b)
prev = cast("DictionaryObject", self["/Last"])
visited: set[int] = set()
while prev.indirect_reference != before:
prev_id = id(prev)
if prev_id in visited:
raise LimitReachedError("Detected cycle in tree structure.")
visited.add(prev_id)
if "/Next" in prev:
prev = cast("TreeObject", prev["/Next"])
continue
# append at the end
prev[NameObject("/Next")] = cast("TreeObject", child_reference)
# ... [omitted logic to update child relationships] ...
return child_referenceThe patched version instantiates an internal state set tracking object memory IDs using id(prev). Because memory addresses of instantiated Python classes remain static during runtime processing, id(prev) functions as an effective tracking key. The loop checks each node against this set; if an ID is already present, it indicates a structural cycle and raises a LimitReachedError to abort execution.
Exploitation requires the attacker to construct a cyclic tree graph within the outline bookmarks or name-tree dictionaries of a PDF structure. This can be accomplished programmatically by modifying standard PDF references. The exploit payload relies on standard binary parsing; there are no memory corruption components, shellcode payloads, or platform-specific compilation constraints needed.
Once the malformed PDF is constructed, the target server or application must process the file through a routine that invokes structural updates on the outlines. Common triggers include page insertion functions (PdfWriter.append or merge), metadata extraction utilities, and bookmark reorganization tasks.
When the processing execution reaches the insert_child block, the thread enters the unconstrained loop. This results in the utilization of 100% of the active CPU thread. In microservices processing PDFs synchronously, this completely freezes the application worker, allowing an attacker to deny service to all other clients by uploading multiple instances of the document.
The impact of CVE-2026-84309 is classified as High for system availability. In common production configurations, Python web workers (such as Gunicorn, Celery, or uWSGI) are configured with a limited pool of synchronous execution threads. A single malformed PDF file can permanently freeze a single worker thread; sending a small quantity of concurrent exploit files can deplete the entire worker pool, resulting in a total denial of service.
The vulnerability has been evaluated with CVSS v4.0 metrics and assigned a base score of 6.9, with a Local Attack Vector (AV:L). Although classified as local, many cloud-native and serverless architectures process PDFs automatically via API gateways or upload portals, effectively extending this vulnerability to remote, unauthenticated actors.
Currently, there is no threat intelligence indicating active exploitation of this vulnerability by ransomware campaigns or advanced persistent threat groups, and the vulnerability is not listed in the CISA KEV catalog. The risk profile is primarily operational, impacting backend processing integrity and service availability.
The recommended remediation is upgrading the pypdf dependency to version 6.16.0 or later. The update introduces the cycle-detection mechanism within the core library code. If an immediate upgrade is impossible, systems administrators can implement execution timeouts to terminate runaway processing threads.
Administrators can implement process timeouts within server configurations. For example, setting strict runtime limits inside Celery tasks or setting lower Gunicorn worker lifetimes ensures that blocked threads are automatically terminated and restarted. Additionally, resource monitoring rules should flag any worker process exhibiting persistent 100% CPU utilization.
To identify vulnerable Python environments, systems security teams can run static detection scans. The following YARA signature can be utilized to scan for vulnerable packages within Python site-packages directories:
rule Detect_Vulnerable_PyPDF_Loop {
meta:
description = "Detects vulnerable pypdf installations lacking cyclic tree checks"
cve = "CVE-2026-84309"
strings:
$pkg = "pypdf"
$func = "def insert_child("
$prev = "prev = cast(\"DictionaryObject\", self[\"/Last\"])"
$patch = "visited: set[int] = set()"
condition:
$pkg and $func and $prev and not $patch
} CVSS:4.0/AV:L/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.16.0 | 6.16.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-835 |
| Attack Vector | Local (AV:L) / Indirectly Network |
| CVSS v4.0 Score | 6.9 |
| Exploit Status | poc |
| CISA KEV Status | Not Listed |
| Impact | Denial of Service (CPU Exhaustion) |
The program contains a loop that can only exit when a specific condition is met, but a path exists where that condition is unreachable, causing the loop to run indefinitely.
An LDAP injection vulnerability exists in the centraldogma-server-auth-shiro module of LY Corporation Central Dogma before version 0.84.0. The search logic dynamically constructs LDAP search filters by interpolating user-provided usernames without escaping RFC 4515 metacharacters. Unauthenticated remote attackers can leverage this flaw to bypass authentication, enumerate directory hierarchies, and access unauthorized resources.
CVE-2026-11746 is a critical vulnerability in Central Dogma Server prior to version 0.84.0, where an embedded ZooKeeper replication secret silently falls back to a publicly known, hard-coded default string ('ch4n63m3'). Remote attackers with access to the replication network can authenticate as legitimate cluster peers, potentially leading to unauthorized data exposure, state manipulation, or complete cluster takeover.
A logical verification flaw in ZITADEL's external JWT Identity Provider validation allows attackers to bypass session expiration checks. If an incoming JWT lacks the 'exp' claim, the system skips validation entirely, creating an indefinitely valid session. This issue has been addressed in versions 3.4.12 and 4.15.2.
CVE-2026-59149 identifies a directory traversal vulnerability in `@mockoon/commons-server`, the backend mock-server library powering the Mockoon application. The flaw occurs in the path containment validation logic used during raw file response generation. An unauthenticated attacker can exploit this weakness to retrieve arbitrary files from sibling directories sharing a common prefix with the designated static base directory.
An in-depth analysis of CVE-2026-59148, a high-severity flaw in Mockoon where unauthenticated administrative endpoints and a wildcard Cross-Origin Resource Sharing (CORS) policy allow remote execution, state poisoning, and credential theft.
An improper authentication vulnerability (CWE-287) in ZITADEL's external identity provider handler before version 4.15.3 allows remote attackers to perform complete account takeover. When auto-linking by email is enabled, ZITADEL verifies that the local target account has a verified email address but fails to verify if the external provider confirmed ownership of that same email. Attackers can exploit this by registering an unverified account with a victim's email address on a permissive external provider, leading to unauthorized account binding and persistent access.