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

CVE-2026-54651: Infinite Loop Vulnerability in pypdf Article Thread Parser

Alon Barad
Alon Barad
Software Engineer

Jul 9, 2026·7 min read·39 visits

Executive Summary (TL;DR)

A denial-of-service vulnerability in pypdf (< 6.13.1) allows attackers to trigger 100% CPU starvation by uploading a PDF containing cyclic Article structure definitions. The parser fails to detect sub-loops within the Thread linked list, resulting in an infinite execution loop. Upgrading to version 6.13.1 implements a visited-set lookup that mitigates this risk.

An infinite loop vulnerability exists in the pure-Python PDF library pypdf prior to version 6.13.1. When parsing or merging a crafted PDF file containing a cyclic Article/Thread structure, the library fails to exit its traversal loop. This causes the executing thread to hang indefinitely, leading to 100% CPU utilization and a denial of service. The vulnerability is tracked under CVE-2026-54651 and GHSA-g9xf-7f8q-9mcj, with a CVSS base score of 5.5. This technical report provides a root cause analysis, code review, exploitation vectors, and mitigation paths.

Vulnerability Overview

pypdf is a widely utilized open-source, pure-Python library designed for parsing, splitting, merging, and mutating PDF files. It acts as a core dependency within multiple enterprise application pipelines, automated ingestion tools, and web-based file management systems. Because it parses complex binary structures, it exposes a substantial attack surface when processing untrusted inputs.

This technical analysis reviews CVE-2026-54651, an infinite loop vulnerability classified under CWE-835 (Loop with Unreachable Exit Condition). The flaw is located within the writer logic of the library, specifically inside the _add_articles_thread function in pypdf/_writer.py. By presenting a PDF with a malformed thread structure, an attacker can trigger CPU exhaustion on the server processing the file.

The vulnerability affects all versions of the library prior to 6.13.1. Applications that accept user-uploaded PDF files and perform write, merge, or modification operations are directly vulnerable. A successful exploit causes complete core starvation on the host machine, making it a reliable mechanism for denial-of-service attacks.

Root Cause Analysis

The PDF standard (specifically Section 12.4.3 of the PDF 1.7 specification) defines "Articles" to establish logical reading paths across columns and pages. These sequences are organized within a document's catalog inside the /Articles array, which points to one or more /Threads dictionaries. Each thread contains a linked list of individual article beads, representing sequential text boxes.

Each article bead dictionary defines a next pointer under the /N key and a previous pointer under the /V key. In standard scenarios, the chain of beads either terminates cleanly (with a null pointer) or forms a complete, closed cycle that links the final bead back to the first bead. The pypdf parser traverses this linked list in the _add_articles_thread method by walking through the nodes via the /N key.

The root cause of the vulnerability lies in the assumption that any cyclic bead chain will return to the first article bead. If an attacker constructs a PDF where the cyclic loop exists only between subsequent nodes (e.g., Bead B points to Bead C, which points back to Bead B), the traversal loop will never reach the termination criteria. The current_article variable never becomes null, and it never matches first_article (Bead A), trapping the execution in an infinite sequence.

Code Analysis

In the vulnerable versions of pypdf (prior to 6.13.1), the traversal was executed using a standard while loop without safety boundaries. The relevant segment of the implementation is shown below:

first_article = cast("DictionaryObject", thread["/F"])
current_article: Optional[DictionaryObject] = first_article
new_article: Optional[DictionaryObject] = None
 
while current_article is not None:
    # ... cloning and mapping logic ...
    # Traverse to the next article node via the /N key
    current_article = cast("DictionaryObject", current_article["/N"])
    # Check if we have looped back to the initial node
    if current_article == first_article:
        new_article[NameObject("/N")] = new_first.indirect_reference
        new_first[NameObject("/V")] = new_article.indirect_reference
        current_article = None  # Exit the loop safely

This implementation fails if a cycle is introduced that excludes the starting node. In the patched release (6.13.1), the library introduces a visited-node set to record the memory identity of processed elements. This mechanism ensures that the parsing environment fails fast upon detecting duplicate references.

# Patched implementation in v6.13.1
visited: set[int] = set()
while current_article is not None:
    # Get memory address ID of the current dictionary object
    article_id = id(current_article)
    # Check for cyclic loops using the visited set lookup
    if article_id in visited:
        raise LimitReachedError("Detected cyclic article structure.")
    visited.add(article_id)
    
    # ... cloning and mapping logic ...
    current_article = cast("DictionaryObject", current_article["/N"])
    if current_article == first_article:
        new_article[NameObject("/N")] = new_first.indirect_reference
        new_first[NameObject("/V")] = new_article.indirect_reference
        current_article = None

The fix is robust because the use of Python's built-in id() lookup ensures fast, identity-based verification during a single processing pass. However, because this fix raises a LimitReachedError exception, calling applications must handle this exception explicitly. If unhandled, the application will exit abruptly, shifting the vulnerability from an infinite loop to an unhandled exception crash.

Exploitation Methodology

To exploit CVE-2026-54651, an attacker must craft a PDF document with a malformed /Threads dictionary. The attack does not require privilege escalation or authentication if the target platform exposes a public endpoint for file ingestion. The main execution constraint is that the file must contain a sub-loop within the structural hierarchy of its article beads.

# Programmatic payload generation using a vulnerable pypdf writer structure
import pytest
from pypdf import PdfWriter
from pypdf.generic import DictionaryObject, NameObject, NullObject
 
writer = PdfWriter()
thread = DictionaryObject()
writer._add_object(thread)
 
# Instantiate three distinct article dictionary objects
art1 = DictionaryObject({NameObject("/P"): NullObject()})
ref1 = writer._add_object(art1)
 
art2 = DictionaryObject({NameObject("/P"): NullObject()})
ref2 = writer._add_object(art2)
 
art3 = DictionaryObject({NameObject("/P"): NullObject()})
ref3 = writer._add_object(art3)
 
# Configure the cyclic hierarchy: Bead 1 -> Bead 2 -> Bead 3 -> Bead 2
thread[NameObject("/F")] = ref1
art1[NameObject("/N")] = ref2
art2[NameObject("/N")] = ref3
art3[NameObject("/N")] = ref2  # Isolated cycle targeting Bead 2, avoiding Bead 1

When a PDF reader or backend server processes this constructed object by merging or writing pages, it calls _add_articles_thread. The parser will continuously iterate between art2 and art3. This behavior utilizes 100% of the CPU core allocated to the executing runtime thread.

In standard deployment configurations, Python web workers (such as Gunicorn or uWSGI) are configured as single-threaded processes. This means that a single malicious request can permanently lock up an entire backend container, preventing other users' requests from being serviced. Repeated requests can exhaust all container instances, leading to an application-wide outage.

Impact Assessment

The primary operational outcome of a successful exploit is localized or system-wide Denial of Service. In virtualized environments, continuous 100% CPU utilization triggers automated scaling alerts. This reaction can lead to automatic cloud autoscaling events, resulting in unexpected financial charges as additional compute nodes are provisioned.

The Common Vulnerability Scoring System (CVSS) v3.1 score is evaluated at 5.5 (Medium), with a vector of CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H. The CVSS v4.0 score, assigned by the CNA, is 6.9, with the difference reflecting the enhanced priority of availability impacts within the v4.0 metrics.

There are currently no records indicating that this vulnerability is actively exploited in the wild, and it is not listed in the CISA Known Exploited Vulnerabilities catalog. The Exploit Prediction Scoring System (EPSS) score remains low at approximately 0.00111. However, because generating the payload is mathematically straightforward, security teams should assume a reliable exploit capability is feasible.

Remediation and Defenses

The definitive remediation for this vulnerability is to update the target systems' dependencies to pypdf version 6.13.1 or newer. This version implements the visited-set lookup that terminates loop execution before resources are exhausted.

# Upgrade using the standard pip package manager
pip install --upgrade pypdf>=6.13.1

If upgrading dependencies is not immediately viable, temporary defensive workarounds should be applied at the infrastructure layer. Enabling container-level CPU limits prevents an infinite loop from degrading the performance of other services running on the same host machine. Setting short execution timeouts (e.g., 30 seconds) on application workers and Celery tasks ensures that hung threads are automatically terminated.

Developers must also update their parsing code blocks to handle the newly introduced exception. Wrapping the parsing function in a try-except block prevents the application from crashing when it processes cyclic structures.

from pypdf.errors import LimitReachedError
 
try:
    # File processing sequence
    writer.write(output_file)
except LimitReachedError:
    # Safely reject the malformed file
    log_security_event("Rejected cyclic PDF structure")

Official Patches

py-pdfOfficial release notes for version 6.13.1 containing the bugfix.
py-pdfOfficial GitHub Security Advisory describing the vulnerability mechanics.

Fix Analysis (1)

Technical Appendix

CVSS Score
5.5/ 10
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.11%
Top 98% most exploited

Affected Systems

pypdf library installations prior to version 6.13.1Web applications utilizing pypdf to merge, append, or modify untrusted PDF files

Affected Versions Detail

Product
Affected Versions
Fixed Version
pypdf
py-pdf
< 6.13.16.13.1
AttributeDetail
CWE IDCWE-835 (Loop with Unreachable Exit Condition)
Attack VectorLocal / File Parsing (AV:L)
CVSS Score5.5 (Medium, CVSS v3.1) / 6.9 (Medium, CVSS v4.0)
EPSS Score0.00111 (Percentile: 1.58%)
ImpactDenial of Service via 100% CPU starvation
Exploit StatusPoC documented, non-weaponized
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1190Exploit Public-Facing Application
Initial Access
CWE-835
Loop with Unreachable Exit Condition ('Infinite Loop')

The program contains an iteration loop or recursive structure where the exit condition cannot be reached, causing the loop to run indefinitely and exhaust resources.

Known Exploits & Detection

GitHub Pull RequestRegression testing block verifying loop-detection mechanics under cyclic configurations.

References & Sources

  • [1]GitHub Pull Request #3839
  • [2]pypdf 6.13.1 Release Changelog
  • [3]GitHub Security Advisory GHSA-g9xf-7f8q-9mcj
  • [4]Fix Commit 5efe472
  • [5]NVD CVE-2026-54651 Detail

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 23 hours ago•CVE-2026-63462
7.5

CVE-2026-63462: Unauthenticated Stack Overflow Denial of Service in Unleash Server

An unauthenticated remote denial of service vulnerability exists in the Unleash feature management platform. By submitting a crafted JSON payload containing deeply nested structures to an OpenAPI-validated endpoint, an attacker can trigger uncontrolled recursion within the error formatting module. This leads to a call-stack exhaustion (RangeError: Maximum call stack size exceeded) inside the Node.js runtime, causing the service to crash immediately without recovery.

Alon Barad
Alon Barad
8 views•6 min read
•1 day ago•CVE-2026-63004
5.5

CVE-2026-63004: Server-Side Request Forgery in Unleash Addon and Integration Subsystem

CVE-2026-63004 is a server-side request forgery (SSRF) vulnerability in the Unleash feature management platform. Authenticated administrators with CREATE_ADDON or UPDATE_ADDON privileges can exploit this vulnerability to initiate requests to loopback addresses, private networks, and cloud metadata endpoints, potentially leading to information disclosure and credential extraction.

Amit Schendel
Amit Schendel
7 views•8 min read
•1 day ago•CVE-2026-63466
4.1

CVE-2026-63466: Process-Wide Security Degradation via Global Module Mutation in Unleash

Prior to version 8.0.3, Unleash's Markdown event formatter directly mutated the global template-escaping function of the shared mustache Node.js module, resulting in a process-wide security degradation where HTML/Markdown escaping was permanently disabled for the application lifetime.

Alon Barad
Alon Barad
3 views•6 min read
•1 day ago•CVE-2026-76904
9.8

CVE-2026-76904: Unauthenticated SQL Injection in GeoTools PostGIS DataStore Component

A critical SQL injection vulnerability exists in the GeoTools open-source Java library. This vulnerability is situated within the post-processing phase of OGC Filter conversion inside the PostGIS DataStore module. Specifically, the `jsonArrayContains` function does not validate or sanitize its arguments before constructing PostgreSQL SQL/JSON path evaluation queries. An unauthenticated remote attacker can exploit this weakness by submitting crafted filters via standard OGC services like WFS or WMS to execute arbitrary SQL commands on the underlying database system.

Alon Barad
Alon Barad
8 views•5 min read
•1 day ago•CVE-2026-61824
8.2

CVE-2026-61824: High-Severity Cross-Site Scripting (XSS) via Unsanitized Site Extractors in Defuddle

CVE-2026-61824 is a high-severity Cross-Site Scripting (XSS) vulnerability in kepano/defuddle before version 0.19.1. Custom site extractors for platforms such as X/Twitter, Substack, and YouTube constructed HTML representations via template string interpolation without output escaping. This allowed malicious pages to bypass standard parser sanitization routines and execute arbitrary JavaScript.

Amit Schendel
Amit Schendel
5 views•7 min read
•1 day ago•CVE-2026-63421
7.5

CVE-2026-63421: Query Limit Bypass via Negative Integer Input in KeystoneJS core resolvers

A high-severity vulnerability exists in KeystoneJS, a popular Node.js CMS and GraphQL framework, where the query resolution engine fails to validate signed negative integers within the pagination subsystem. Unauthenticated remote attackers can leverage this flaw to bypass the 'graphql.maxTake' safety boundary. By sending large negative values in the 'take' query parameter, the underlying Prisma ORM interprets the value as an instruction to fetch rows from the end of the collection, allowing malicious actors to bypass pagination limits, trigger database resource exhaustion, and execute application-level Denial of Service (DoS) attacks.

Alon Barad
Alon Barad
5 views•6 min read