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

Ouroboros in the Outline: Infinite Loops in pypdf (CVE-2026-24688)

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 27, 2026·6 min read·44 visits

Executive Summary (TL;DR)

The `pypdf` library (< 6.6.2) fails to detect cycles when parsing PDF outlines (bookmarks). An attacker can craft a malicious PDF where bookmark A points to bookmark B, and bookmark B points back to A, causing the parser to enter an infinite loop. This effectively hangs the application, consuming all available CPU resources.

A Denial of Service (DoS) vulnerability in the popular `pypdf` library allows attackers to trigger an infinite loop by crafting a PDF with cyclic outline references. This results in 100% CPU utilization and application hangs.

The Hook: A Snake Eating Its Own Tail

PDF parsing is a thankless job. You are essentially writing code to interpret a file format that is less of a document and more of a serialized hallucination of Adobe engineers from the 1990s. One of the most common pitfalls in parsing complex, hierarchical data structures is assuming that a tree is actually a tree.

In CVE-2026-24688, we look at pypdf, a wildly popular pure-Python library used for everything from splitting pages to extracting text. The vulnerability here isn't a buffer overflow or a remote code execution via pickle serialization. It's a logic error—a classic infinite loop caused by trusting the input.

The specific component at fault is the Outline parser. In PDF terminology, 'Outlines' are what users see as Bookmarks. They are a navigational aid. But to a parser, they are a linked list of dictionary objects. And whenever you have a linked list provided by untrusted user input, you have to ask yourself one question: 'What happens if this list is actually a circle?'

For pypdf versions prior to 6.6.2, the answer to that question was 'I will run until the heat death of the universe or until the sysadmin kills the process.'

The Flaw: Trusting the Linked List

To understand the flaw, we have to look at how PDF outlines are structured. Internally, a PDF outline is a collection of dictionary objects. Each object points to its neighbors using keys like /First (the first child), /Last (the last child), /Next (the next sibling), and /Parent (the parent node).

It looks something like this in a healthy file:

The vulnerability lies in pypdf/_doc_common.py, specifically in the _get_outline method. This function is responsible for traversing this structure to build a Python-friendly list of bookmarks. It uses a while True loop to iterate through siblings via the /Next key and recursive calls to handle children via /First.

The fatal mistake? The code assumed that following /Next pointers would eventually lead to a null or missing value, terminating the loop. It did not track which nodes it had already visited. If a malicious actor hand-edits a PDF so that NodeB's /Next pointer goes back to NodeA, the parser happily obliges, running in circles forever.

This is a textbook Denial of Service (DoS) via resource exhaustion. It’s stateless, requires no authentication, and can be triggered by simply asking the server to "read the bookmarks" of a tiny, 1KB PDF file.

The Code: The Smoking Gun

Let's look at the vulnerable code in pypdf/_doc_common.py. I've stripped it down to the essentials to highlight the logic flaw.

# VULNERABLE CODE (< 6.6.2)
def _get_outline(self, node, outline):
    while True:
        # 1. Process the current node
        outline_obj = self._build_outline_item(node)
        outline.append(outline_obj)
        
        # 2. Check for children (Recursion)
        if "/First" in node:
            sub_outline = []
            self._get_outline(node["/First"], sub_outline)
            outline.append(sub_outline)
            
        # 3. Move to next sibling
        if "/Next" in node:
            node = node["/Next"] # <--- THE TRAP
        else:
            break

Notice the node = node["/Next"] line inside a while True loop. There is absolutely no guardrail here. If node["/Next"] is the node itself, node never changes, and the loop spins tight. If it points to a previous node, it loops wide.

The fix, introduced in version 6.6.2, is elegant in its simplicity. It introduces a visited set that tracks the memory IDs of the processed objects. If we see an ID we've already processed in the current chain, we bail out.

# PATCHED CODE (6.6.2+)
def _get_outline(self, node, outline, visited=None):
    if visited is None:
        visited = set()
        
    while True:
        # 1. CYCLE DETECTION
        node_id = id(node)
        if node_id in visited:
            logger.warning(f"Detected cycle in outline for {node}")
            break
        visited.add(node_id)
 
        # ... processing code ...
 
        if "/First" in node:
             # Pass a COPY of visited to children to allow valid DAGs
             # but prevent cycles down the tree
             self._get_outline(..., visited=visited.copy())

The Exploit: Crafting the Ouroboros

Exploiting this doesn't require complex heap spraying or ROP chains. It requires a text editor. PDFs are partially ASCII, and their structure is defined in plain text blocks.

Here is how an attacker constructs a "bomb":

  1. Create a standard PDF. Any "Hello World" PDF will do.
  2. Locate the Outline dictionary. It usually looks like << /Type /Outlines ... >>.
  3. Inject a Cycle. We create two objects, 5 and 6, and link them together eternally.
5 0 obj
<<
  /Title (Bookmark A)
  /Parent 4 0 R
  /Next 6 0 R  % Points to B
>>
endobj
 
6 0 obj
<<
  /Title (Bookmark B)
  /Parent 4 0 R
  /Prev 5 0 R
  /Next 5 0 R  % Points back to A!
>>
endobj

When pypdf hits Object 5, it follows /Next to Object 6. It processes Object 6, follows /Next back to Object 5. Repeat ad infinitum.

The impact is immediate. If this runs in a web worker (e.g., a "Upload your Resume" feature that extracts text or checks page counts), that worker thread hangs at 100% CPU. If you upload 10 of these files, you take down 10 workers. It is a highly asymmetric attack: trivial to generate, expensive to mitigate without the patch.

The Fix: Mitigation & Defense

The remediation is straightforward: Update pypdf to version 6.6.2 or later.

However, this vulnerability highlights a broader issue in handling complex file formats. If you are processing files from untrusted sources, relying solely on library patches is often a game of whack-a-mole.

Defense in Depth Strategies:

  1. Timeouts: Never let a file parsing job run indefinitely. Wrap your parsing logic in a timeout block (e.g., Python's func_timeout or generic task queue timeouts in Celery/RQ). If parsing a 2MB PDF takes more than 10 seconds, kill it.
  2. Resource Limits: Run your parser in a containerized environment (Docker/Kubernetes) with strict CPU and RAM limits. This prevents a single hung process from starving the entire host.
  3. Input Validation: While you can't easily detect cycles without parsing, you can validate file headers and enforce maximum recursion depths if you are wrapping the library yourself.

Official Patches

pypdfPull Request #3610: SEC: Detect cyclic references when retrieving outlines
pypdfpypdf 6.6.2 Release Notes

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

Affected Systems

pypdf < 6.6.2Applications using pypdf for outline/bookmark extractionPDF processing pipelinesWeb applications accepting PDF uploads

Affected Versions Detail

Product
Affected Versions
Fixed Version
pypdf
py-pdf
< 6.6.26.6.2
AttributeDetail
Vulnerability IDCVE-2026-24688
CWE IDCWE-835
TypeInfinite Loop / DoS
CVSS7.5 (High)
Attack VectorNetwork (File Upload)
Patch Date2026-01-26

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.003Application or System Exploitation
Impact
CWE-835
Infinite Loop

Loop with Unreachable Exit Condition ('Infinite Loop')

Known Exploits & Detection

GitHubProof of Concept PDF file with circular outline references provided in the issue tracker.

Vulnerability Timeline

Vulnerability identified
2026-01-25
Fix committed to repository
2026-01-26
pypdf version 6.6.2 released
2026-01-26
CVE-2026-24688 published
2026-01-26

References & Sources

  • [1]GitHub Advisory GHSA-2q4j-m29v-hq73
  • [2]pypdf Documentation

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

•about 12 hours ago•CVE-2026-39922
6.3

CVE-2026-39922: Server-Side Request Forgery in GeoNode Service Registration Endpoint

GeoNode versions prior to 4.4.5 and 5.0.2 are vulnerable to Server-Side Request Forgery (SSRF) in the service registration endpoint. Authenticated attackers with low privileges can exploit insufficient input validation in the Web Map Service (WMS) registration module to force the application server to make outbound network queries to loopback addresses, private RFC1918 subnets, link-local scopes, and cloud metadata endpoints. This technical report details the mechanics of the vulnerability, the underlying architectural flaw, and how to effectively remediate and mitigate the associated security risks.

Alon Barad
Alon Barad
4 views•7 min read
•about 21 hours ago•CVE-2022-0492
7.8

CVE-2022-0492: Privilege Escalation and Container Escape via cgroups v1 release_agent

CVE-2022-0492 is a high-severity missing authorization vulnerability in the Linux kernel's Control Groups (cgroups) v1 implementation. The flaw resides within the cgroup_release_agent_write function in kernel/cgroup/cgroup-v1.c, where the kernel fails to validate if the process writing to the release_agent file possesses administrative capabilities in the initial user namespace. This allows a local attacker inside a container with root privileges (UID 0) to abuse user namespaces, mount a cgroups v1 directory, modify the release_agent parameter, and execute arbitrary commands on the host system as host root, effectively achieving a complete container escape.

Amit Schendel
Amit Schendel
8 views•7 min read
•3 days ago•GHSA-G72G-R7M4-9X4G
6.3

GHSA-G72G-R7M4-9X4G: Insufficient Session Expiration of OAuth Tokens in NocoDB

NocoDB is subject to an insufficient session expiration vulnerability where OAuth access and refresh tokens are not invalidated or revoked during security-sensitive actions such as password changes, forgot-password requests, or password resets. This allows an attacker possessing an active OAuth token to maintain unauthorized persistence.

Amit Schendel
Amit Schendel
12 views•6 min read
•3 days ago•GHSA-FGMC-2HQJ-86V4
6.9

GHSA-FGMC-2HQJ-86V4: Default Administrative Credentials in vantage6-server

A vulnerability in the vantage6 federated learning framework allows unauthenticated remote attackers to gain administrative control of the server via hardcoded default credentials (root/root) when deployed under default configurations in versions 4.2.3 and below.

Amit Schendel
Amit Schendel
8 views•5 min read
•3 days ago•GHSA-X9F6-9RVM-MMRG
6.9

GHSA-X9F6-9RVM-MMRG: Improper Access Control and Volume Mount Isolation Bypass in vantage6 Node

An improper access control vulnerability in the vantage6 node component allows concurrently running algorithm containers to read and modify sensitive input and output files of other tasks. The lack of strict workspace directory isolation exposes a significant attack surface in multi-tenant or federated environments where untrusted algorithms are executed.

Amit Schendel
Amit Schendel
3 views•4 min read
•3 days ago•CVE-2026-47760
8.7

CVE-2026-47760: Cross-Site Scripting (XSS) via SVG Namespace Sanitizer Bypass in TinyMCE

TinyMCE versions 6.8.0 through 7.0.1 contain a high-severity Cross-Site Scripting (XSS) vulnerability. The flaw exists in the custom HTML parser and sanitizer module, which incorrectly manages SVG namespace scopes when parsing nested elements. A low-privileged or unauthenticated attacker can submit a crafted HTML payload containing nested SVG structures to bypass sanitization filters, leading to arbitrary JavaScript execution in the context of the victim's browser session.

Alon Barad
Alon Barad
30 views•7 min read