
Node2Shell: Reproducing CVE-2026-111497 in the Node.js Brotli decoder
Node2Shell is Datadog Security Labs' analysis of CVE-2026-111497, an out-of-bounds write in the Google Brotli 1.2.0 decoder, fixed in Node.js 22.23.4, 24.22.1 and 26.12.1 on 21 October 2026. It explains the unchecked return value in DecodeBlockTypeAndLength(), the read and write primitives, and why code execution is still engineering effort. It lists inbound and outbound exposure paths, behavioral detections and what WAFs miss.
What it is
CVE-2026-111497 is an out-of-bounds write in the Google Brotli C library (version 1.2.0), reachable through Node.js and other runtimes that embed it. The CVE record gives CVSS v4.0 9.2 (Critical), while Node.js rates it High. Fixes shipped on 21 October 2026 in Node.js 22.23.4, 24.22.1 and 26.12.1. End-of-life Node.js lines carry the same decoder and have no patch. Datadog reports confirmed in-the-wild exploitation as of 29 October 2026, and the CVE is in CISA's known exploited vulnerabilities catalog. A public proof of concept appeared on 22 October 2026 and gives a crash and a limited memory read on standard builds.
How it works
DecodeBlockTypeAndLength() returns a boolean, and false means both "malformed stream" and "need more input". On the fast path the return value is dropped. A 23 MB stream drains the block length budget with no output, then requests a block switch the format forbids. Two outcomes follow:
- Overflow: unconsumed bytes are copied into a small internal buffer with no bounds check and overwrite adjacent decoder state. The write stays inside one heap allocation, so memory-corruption tooling does not see it and the process keeps running.
- Spin: the decoder retries the forbidden switch forever, pinning one CPU core in a call that never returns. This is a one-request denial of service.
The corrupted state gives a repeatable heap read, returned in the decompressed output, and a redirected write (aimed at an unmapped page in the lab, giving a deterministic crash). Datadog names three obstacles to code execution: randomized heap addresses, corruption bytes that are also decoder input, and the managed runtime. It judges the gap to be engineering effort. Imperva reportedly achieved code execution on the official Node.js 24.22.0 Linux ARM64 image after several attempts and did not release the exploit.
flowchart LR A[Inbound body with Content-Encoding br] --> D[Brotli decoder in app process] B[Outbound response labeled br] --> D D --> E[Overflow: heap read, crash, possible RCE] D --> F[Spin: 100% CPU, DoS]
Preconditions / Affected
A service is exposed if it decompresses attacker-controlled Brotli data.
- Inbound: Express 5 and body-parser 2.x accept Brotli bodies by default in their JSON, raw, text and URL-encoded parsers. Koa and NestJS inherit this. Express 4 returns HTTP 415, and a bare Node.js HTTP server does not decompress. Parsers mounted before authentication expose the decoder to unauthenticated requests.
- Outbound: Node's
fetch()decodesContent-Encoding: brresponses automatically, even when the app forcesAccept-Encoding: identity. The axios, got and node-fetch clients decode Brotli by default. Features that fetch user-supplied URLs (webhooks, link previews, feed readers) let the attacker control the response. In testing, one crafted stream ended with an empty body and HTTP 200, with no exception. - Other runtimes: Python, Bun, .NET, Java brotli4j, Envoy and Istio are affected. Python clients decode Brotli whenever Brotli or brotlicffi is installed.
- Traps: brotli4j bundles its own native build, so patching the OS libbrotli does not help. Bun executables built with
bun build --compilestay vulnerable until rebuilt.process.versions.brotlistill reports 1.2.0 on patched Node.js 24.22.1, so checkprocess.version, and only for official nodejs.org builds.
What to do
Patch to Node.js 22.23.4, 24.22.1 or 26.12.1, or to your vendor's fixed package. Rebuild container images, restart every process, and inventory other Brotli consumers.
If you cannot patch yet, set this on Express 5 and body-parser 2.x parsers, or reject Content-Encoding: br at the reverse proxy:
inflate: falseDetection relies on behavior, since there are no attacker infrastructure indicators:
- Decoder errors: a vulnerable Node.js build returns
Z_BUF_ERRORwith "unexpected end of file" (benign truncated input produces it too). A patched build returnsERR__ERROR_FORMAT_BLOCK_SWITCH(format error -17), and Python returns "brotli: decoder is unhealthy". - Process signals: exit with signal 139 (SIGSEGV) right after a Brotli transfer, or a process pinned at 100% CPU inside decompression. Correlate by workload (container, pod or service), not PID.
- Strongest signal: a shell or new outbound connections from the same workload.
- Disclosure channel: decompressed output containing bytes the client never sent. Start with endpoints that echo, preview, convert or proxy decompressed content.
Datadog lists its own coverage: Workload Protection content pack, two Cloud SIEM shell-spawn detections, and APM and Code Security pivots.
Caveats
- The
inflate: falseand proxy mitigations break legitimate compressed request bodies and do nothing for the outbound path. Datadog says only patching fully fixes the flaw. - WAFs, payload byte signatures and body-size limits do not work. The proxy never decodes the body, the stream is per-target, and the tested middleware counts decompressed bytes, so the 23 MB compressed trigger still gets through. A rule rejecting Brotli request bodies is only a tripwire.
- The fix was committed upstream on 27 February 2026 but not released until 21 October 2026. The release has no regression test or changelog entry for it.
- Datadog's own lab reproduction yielded a crash and a read, not code execution. Its code-execution assessment rests on its analysis plus Imperva's unreleased report.
Organisations named here
- ERR__ERROR_FORMAT_BLOCK_SWITCH
- Imperva
- out-of-bounds write
CVEs named here
- CVE-2026-111497
Technologies named here
- Node.js
- Datadog Code Security
- Python
- Bun
- .NET
- Datadog Cloud SIEM
- CISA Known Exploited Vulnerabilities Catalog
- Envoy
- Istio
- Express 5 and body-parser 2.x
- Brotli
- Java
- CVSS
- CISA KEV
- Common Vulnerability Scoring System (CVSS) v4.0
- Datadog Agent
- Datadog Application Performance Monitoring (APM)
- Datadog Cloud Security Management
- Datadog Workload Protection
- Google Brotli C library 1.2.0 (upstream flaw; fix committed February 27, 2026, fixed releases shipped October 21, 2026)
- Imperva
- Java brotli4j (bundles its own native Brotli build in its jars; OS libbrotli patch does not fix it)
- Node.js fetch(), axios, got, node-fetch (decode Brotli responses)
Techniques named here
- Attacker-controlled Brotli response via user-supplied URLs (outbound path)
- Brotli-compressed request body (inbound path)
- Denial of service via decoder spin loop
- Heap memory read primitive
- Out-of-bounds write in Brotli decoder (DecodeBlockTypeAndLength return value ambiguity)
- Redirected write primitive
Threat actors named here
- Node2Shell
Related in AppSec
Flowise MCP Security Bypass Enables RCE
Inside an Oracle Database SQL Injection Attack | Huntress
OASIS
