Executive Summary
Node2Shell is the name given by Imperva Threat Research to CVE-2026-111497, a high-severity out-of-bounds write vulnerability in the Brotli decoder bundled with Node.js. The flaw allows a remote unauthenticated attacker to trigger memory corruption by supplying a specially crafted Brotli-compressed stream. In favorable conditions this can lead to remote code execution (RCE).
The vulnerability resides in Google’s Brotli library (versions 1.0.3 through 1.2.0) and affects Node.js release lines 22.x, 24.x, and 26.x. It was fixed in Node.js 22.23.4, 24.22.1, and 26.12.1. Imperva demonstrated actual code execution on the official Node.js 24.22.0 Linux arm64 image by delivering a payload inside Brotli data; the same inputs were rejected by the patched 24.22.1 release.
Background: Brotli and Node.js
Brotli is a general-purpose compression algorithm developed by Google (RFC 7932). It is widely used on the web via the Content-Encoding: br header. Node.js ships a copy of the Brotli library under deps/brotli and exposes it through the node:zlib module (zlib.brotliDecompress, createBrotliDecompress, etc.).
The Brotli decoder is a complex state machine. A stream is divided into metablocks. Inside each metablock the decoder maintains block types for literals, commands, and distances. Each block type has its own Huffman tree. A small ring buffer (block_type_rb) tracks recent block-type indices and is used to select the correct Huffman tree for the current context.
Root Cause Analysis
The vulnerability is an unchecked return value (CWE-252) that results in an out-of-bounds write (CWE-787). It lives in c/dec/decode.c of the Brotli library.
The Faulty Check
Inside DecodeBlockTypeAndLength the decoder returns BROTLI_FALSE when a block-type switch is attempted but the metablock header declared that no block-type tree exists (max_block_type ≤ 1):
if (max_block_type <= 1) {
return BROTLI_FALSE; // no block-type tree, but switch requested
}
Ignored Return Value
In the fast (non-safe) path the BROTLI_SAFE macro discards the return value:
#define BROTLI_SAFE(METHOD) \
{ if (safe) { if (!Safe##METHOD) { ... } } \
else { METHOD; /* return value discarded */ } }
Stale State and Out-of-Bounds Access
When the error is ignored the decoder continues with stale state. It does not refresh block_length, does not update htree_command, and then executes:
s->htree_command = s->insert_copy_hgroup.htrees[s->block_type_rb[3]];
block_type_rb[3] is a stale index. When num_block_types[1] == 1 the array of Huffman-tree pointers has only one valid entry. Any index ≥ 1 is out of bounds. This produces an out-of-bounds read or write, which is the memory-safety violation described by both Node.js and Google.
Attack Surfaces
The vulnerable decoder is reachable from two common paths:
A bare Node.js HTTP server does not decompress Brotli request bodies by itself. Exposure on the request path therefore depends on middleware. The root cause is the runtime, not the middleware.
Connection to Raven’s 14 Most Powerful Families in Software
Node2Shell is a textbook illustration of why certain library families sit at the center of application risk. Raven’s original research (The 14 Most Powerful Families in Software, 2026) maps open-source libraries by the sensitive capabilities they introduce rather than solely by known CVEs. The Brotli decoder, the Node.js zlib binding, and Express/body-parser land squarely in three of the most sensitive families.
The Customs Officer (Family 10) — Parsing, Decoding & Deserialization of Untrusted Input
Raven defines this family as libraries that “parse, decode or deserialize attacker-controlled bytes and strings: compression and archive codecs, image, media and document decoders, structured-data parsers…”
"Every byte arriving from the outside passes through its hands. It decides what the package becomes.”
Territory explicitly includes “Decompression codecs.” Node2Shell is precisely this: hostile Brotli bytes reach a complex native decoder that turns them into in-memory structures (or, in this case, corrupted memory). The report’s practical cases include “A compressed bomb exhausts memory…” and “A malformed image or document triggers native memory corruption.”
The Smuggler (Family 08) — Modules with Native Code
The Brotli implementation lives in native C code bundled inside the Node.js runtime (deps/brotli + node_zlib.cc). Raven calls this family “The Smuggler” because it “carries goods across the border between managed and native code — where the runtime’s guarantees don’t follow.”
“Malformed input triggers an overflow, use-after-free or out-of-bounds access.”
This matches the CVE description almost verbatim. The managed JavaScript layer (V8) cannot protect against the memory-safety issue that occurs inside the native decoder.
The Doorman (Family 04) — Server-Side Application & RPC Frameworks
Express and body-parser (including express.json()) are classic members of this family. They own the front door: they receive raw external traffic, parse the request, and decide which internal handler (or decoder) receives the body. body-parser 2.x added automatic Brotli decompression for Content-Encoding: br, which is how untrusted request bodies reach the vulnerable decoder at scale.
“Nothing enters the building without passing its door — and it decides which room you end up in.”
Together these three families explain why the vulnerability has such a large blast radius: a widely deployed Doorman automatically feeds untrusted data into a Customs Officer that is implemented as a Smuggler (native code).
Vulnerability Flow with Raven Family Mapping

Conceptual Call Stack
No public full RCE crash dump has been released. The following is a conceptual call stack for the request path, derived from source analysis of Node.js and the Brotli library:
#0 DecodeBlockTypeAndLength() // decode.c — returns FALSE (ignored)
#1 DecodeCommandBlockSwitchInternal() // decode.c
#2 DecodeCommandBlockSwitch() // decode.c
#3 ProcessCommands / DecodeMetaBlock // decode.c — continues with stale state
#4 BrotliDecoderDecompressStream() // decode.c — out-of-bounds write
#5 BrotliDecoderContext::DoThreadPoolWork() // node_zlib.cc
#6 CompressionStream::Write() // node_zlib.cc
#7 zlib.brotliDecompress / stream // JS layer (node:zlib)
#8 body-parser / express.json() // middleware
#9 HTTP request handler // application
Imperva Proof-of-Concept Findings
Imperva tested the official Node.js 24.22.0 Linux arm64 container image. They delivered a crafted Brotli payload (either in a request body processed by Express/body-parser or in a response consumed by fetch()). After processing, a marker file appeared on disk even though the application contained no code to create that file — confirming code execution inside the Node.js process.
The same inputs were rejected by Node.js 24.22.1; no marker was created and the server remained running. Execution was not successful on every attempt but was reproducible in the tested configurations (Express 5.0.0–5.3.0 with body-parser 2.0.0–2.3.0, and the built-in fetch() path).
The Fix
The upstream fix is Google Brotli commit 0ed4268 (“Detect and report invalid stream earlier”). It changes DecodeBlockTypeAndLength to return the dedicated error code BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH and propagates that error all the way out of the decoder instead of ignoring it. Node.js cherry-picked the commit and exposed the new error constant in node_zlib.cc.
Patched Node.js versions:
Recommendations
- Upgrade immediately to Node.js 22.23.4, 24.22.1, or 26.12.1 (or later).
- Audit Docker images, CI runners, serverless runtimes, and any environment that pins an older Node.js version.
- If you are on an EOL release line (20.x or earlier), migrate to a supported line.
- Until the upgrade is complete, consider blocking Content-Encoding: br at the edge (WAF / reverse proxy). Note that this does not protect applications that call external APIs via fetch().
- Monitor for anomalous Brotli traffic or unexpected process crashes that could indicate exploitation attempts.
Conclusion
CVE-2026-111497 is a classic example of an unchecked return value inside a complex state machine that escalates into memory corruption. Because Node.js both embeds Brotli and automatically invokes it on common paths (Express body parsing and fetch()), the attack surface is large. Imperva’s demonstration of code execution on an official Node.js image confirms that the vulnerability is practical, not merely theoretical.
As with the recent PHP2Shell issues, this incident underscores that application security depends on the safety of the underlying runtime. Organizations should treat Node.js security releases with the same urgency as operating-system patches.
Further Reading: Raven’s 14 Most Powerful Families
The analysis above draws on Raven’s original research that identifies the fourteen library families with the greatest influence over modern application behavior. The full report contains detailed dossiers for each family, capability paths, runtime interrogation questions, and the case for library-level runtime attribution.
Download the free research: https://raven.io/the-14-most-powerful-families-in-software
“Code this powerful should be treated with respect — and it should never operate unseen.”
References
- Node.js Security Releases — October 2026: nodejs.org/en/blog/vulnerability/october-2026-security-releases
- Imperva: Node2Shell — From Brotli Data to Remote Code Execution (imperva.com/blog)
- Google Brotli commit 0ed4268 — “Detect and report invalid stream earlier”
- CVE-2026-111497 (Google CNA / NVD)
- Raven Research: The 14 Most Powerful Families in Software (2026) — raven.io/the-14-most-powerful-families-in-software

