Low Severity CVE-2026-111497
Published: Nov 1, 2026

[GitHub] CVE-2026-111497-brotli-pwn

Other
Type
N/A
CVSS Score
3
Views
Anonymous
Author

Description

CVE-2026-111497: Node.js Brotli Decoder Out-of-Bounds Write — Analysis, PoC, Docker Lab

# CVE-2026-111497: Node.js Brotli Decoder Out-of-Bounds Write

> Chinese version above, English version below.

This repository documents our reproduction and analysis of CVE-2026-111497. All experiments were conducted locally or on self-built test machines, with no access to any third-party systems.

## Summary of Findings

| Capability | Result | Conditions |
|---|---|---|
| Local Command Execution | ✅ 3/3 reproducible | Node + unpatched libbrotlidec + deterministic layout (`--predictable` or ASLR disabled) |
| Denial of Service | ✅ 86-byte stream sufficient | Any unpatched version, no high-volume traffic required |
| Remote Overflow Trigger | ✅ Demonstrated | Target process accepts two attacker-controlled writes |
| Remote Process Memory Read | ✅ In inspector-exposed scenarios | Process running with `--inspect` |
| Remote Blind RCE | ❌ Not viable | With ASLR enabled, without `--predictable`, wrap compensation is unpredictable |

The last point is not due to lack of effort, but because this vulnerability itself only provides "16-byte writes relative to ring," without built-in address leak; public PoCs also stop at this point. Section 5 details the specific bottleneck.

## 1. The Vulnerability

The Brotli decoder built into Node.js, in the streaming path of `BrotliDecoderDecompressStream`, lacks length validation when copying to the tail of `BrotliDecoderState.buffer.u8`.

- Affected: Node 22.x < 22.23.4, 24.x < 24.22.1, 26.x < 26.12.1; EOL branches no longer patched.
- Fix commit: `google/brotli@0ed4268a4401abcb4472096340477cf4af7d1a2b` ("Detect and report invalid stream earlier"), with Node side also exposing `BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH` in `src/node_zlib.cc`.
- Earliest reproducible commit: `34f020fa9daba975747b29885314c2545b7aa040`.

Triggering requires a stream approximately 23 MB long: first, a 16 MiB compressed metablock filled with 2^24 dictionary transform commands that produce zero output, exhausting `block_length[2]`; then the decoder returns `NEEDS_MORE_INPUT` but has already consumed the input. Writing a small segment afterward copies attacker bytes into the 8-byte `buffer.u8[]`, crossing structure boundaries.

## 2. Environment Requirements

- Linux x86-64;
- Node.js 22/24/26, with `libbrotlidec.so.1` still at unpatched version (≤ 1.2.0 / 1.1.x). Ubuntu 24.04's bundled `nodejs` 22.22.1 dynamically links system brotli and can be used directly for reproduction;
- `gcc`, `python3` (for stream generation and compilation);
- Ability to read `/proc/self/mem` (PoC uses it to locate decoder state and libc);
- Deterministic layout: `setarch x86_64 -R node ...` or `node --predictable`.

Check if the target uses dynamic linking:

```console
$ ldd "$(command -v node)" | grep brotli
libbrotlidec.so.1 => /usr/lib/x86_64-linux-gnu/libbrotlidec.so.1
$ node -p "process.versions.brotli"
1.2.0
```

If `ldd` shows no `libbrotlidec` (common with official static builds), the vulnerability path typically cannot be reached; switch to a distribution-packaged Node first.

## 3. Reproduction

Generate trigger stream (approximately 23 MB, not included in repository):

```console
$ python3 poc/gen_stream.py stAA536.br "$(python3 -c 'print("aa"*536)')"
wrote=stAA536.br bytes=23069229 sha256=...
```

Run local PoC:

```console
$ PWNCMD='printf CVE-2026-111497 > /tmp/pwn_marker.txt' PK=4 \
node --predictable poc/pwn_local.js
```

Translated to English from Chinese. Show original · see the source links.
Community Rating
0

Login to rate this exploit

Quick Actions