Back to Blog
Insights

Node2Shell (CVE-2026-111497) — Brotli Decoder RCE in Node.js, Explained

Node2Shell (CVE-2026-111497) — Brotli Decoder RCE in Node.js, Explained

On 21 October 2026, Node.js shipped its October security releases, fixing ten vulnerabilities. One of them outranks the rest: CVE-2026-111497, an out-of-bounds write in the Brotli decoder compiled into Node.js, which researchers demonstrated as unauthenticated remote code execution on stock Node.js builds. Imperva's Red Team, who named it Node2Shell, ran arbitrary code on the official node:24.22.0 Docker image through a single crafted request — with express.json() doing the decompression. Three days later the story stopped being theoretical: CISA added it to the KEV catalog on 27 October based on evidence of active exploitation, and honeypots logged real attack attempts. If you run Node.js in production, this is an upgrade-and-restart event with a federal deadline attached.

To be clear about provenance: IKZERO did not discover this vulnerability. This is our technical read of the Node.js advisory, the Imperva and ELLIO research, and the CISA/MSRC/Envoy guidance — written for teams who ship Node.js to production.

The bug in one paragraph

Google's Brotli decompression library (versions 1.0.3 through 1.2.0) contains an out-of-bounds write in its decoder (c/dec/decode.c). Node.js bundles Brotli, so any Node.js code path that decompresses attacker-controlled Brotli data hands the decoder untrusted input in a language-optimized C routine. Overflow it and you get memory corruption with a documented path to code execution — or, more cheaply, a process crash. The same bug reached far beyond Node; Aikido coined the name HTTP2Shell for the wider ecosystem impact, which we cover in a companion post.

Where an attacker's data reaches the decoder

The exploitability question is never "is my Node version vulnerable" (if it's below the fix, it is) — it's "can attacker input reach brotliDecompress?" In practice, three common paths:

  1. Request bodies with Content-Encoding: br. Express 5 / body-parser 2.x decompress Brotli-encoded request bodies transparently. Imperva's reproduction needed nothing more than a default app.use(express.json()) — one unauthenticated POST, code execution on the official Docker image.
  2. fetch() responses. Node's built-in fetch decompresses Brotli responses automatically. If your server fetches URLs an attacker can influence — webhooks, SSRF-prone fetches, URL previews — the remote server's response becomes your attack input.
  3. Direct node:zlib use. Any brotliDecompress* call on untrusted data is a direct sink. Brotli-in-user-content (compressed uploads, encoded payloads, logs) is in scope.

If none of those describe your app, your practical exposure is lower — but the crash primitive applies wherever the decoder runs, and "we don't decompress anything" deserves a code-level check, not an assumption.

Are you affected?

RuntimeStatus
Node.js 22.x < 22.23.4Vulnerable → upgrade to 22.23.4
Node.js 24.x < 24.22.1Vulnerable → upgrade to 24.22.1
Node.js 26.x < 26.12.1Vulnerable → upgrade to 26.12.1
Node.js 20.x, 23.x, 25.x and olderNo fix exists. EOL lines must move to a supported release
Bun < 1.4.4Vulnerable → upgrade to 1.4.4

Severity context: the Google CNA/NVD scores it CVSS v4.0 9.2 Critical; the Node.js project rates it High. Both point the same direction operationally: it is in CISA's KEV catalog, so patch on KEV timelines, not opinions.

Two things teams forget after upgrading a runtime:

  • Restart everything. PM2/Docker/Kubernetes processes keep the old binary and its old Brotli in memory until restarted; rebuild container images on patched bases rather than patching in place.
  • The same release fixed nine other CVEs — TLS hostname/IP validation bypasses (CVE-2026-106069 et al.), a DNS NUL-truncation bug, and a CA-store refresh related to the eMudhra incident. If you only cherry-picked the Brotli fix, revisit the release notes.

What exploitation actually looks like

The research gives defenders an unusually concrete picture:

  • It works on defaults. ELLIO reproduced code execution on unmodified official x86-64 Node.js 24.22.0 binaries, ASLR on, against four targets: a plain node:http server, the node:24.22.0-slim image running as root, Express 5.3.0 and Koa 3.
  • It's build-specific. Working offsets for 24.22.0 failed on 22.23.3 and 26.12.0. Expect attackers to carry offsets per runtime version, the way they do for browser exploits.
  • Failed attempts still crash. An in-the-wild 23 MB exploit aimed at 24.22.0 failed to get a shell in 50 lab replays — but crashed the process every time. Availability is a casualty even when RCE fails, and crash-looping production services are both an outage and an exploitation signal.
  • Scanning is underway. A scanner identifying itself as node2shell-check was already probing which hosts decode Brotli before the first exploitation attempts.

In the wild: CISA added CVE-2026-111497 to KEV on 27 October; ELLIO recorded exploit traffic in the wild (from an IP previously seen sending PHP2Shell exploits — the same operators are clearly collecting both), and honeypots logged a confirmed attempt the same day. CISA set a 10 November deadline for US federal agencies. Public PoCs are on GitHub.

What to do, in order

1. Upgrade the runtime and restart. 22.23.4 / 24.22.1 / 26.12.1, Bun 1.4.4. In containerized estates, rebuild images from patched bases and redeploy — restarting a container whose image still contains vulnerable Brotli fixes nothing. Verify with node --version inside the running container, not in your CI logs.

2. Inventory the decompression surface. Search your code and dependencies for the three sinks: body-parser/Brotli request handling, fetch() of attacker-influenceable URLs, direct node:zlib Brotli calls on untrusted input. Fix the design problems this surfaces (why is an unauthenticated body decoder on?), not just the CVE.

3. If you cannot patch yet, disable Brotli decoding of untrusted input at the edge:

# strip Brotli from upstream requests — nginx proxy in front of Node
proxy_set_header Accept-Encoding "gzip";
# reject Brotli-encoded request bodies
if ($http_content_encoding ~* "br") { return 415; }

This is a stopgap: it protects the HTTP paths, not direct node:zlib usage, and it breaks legitimate Brotli request bodies. Patch properly and quickly.

4. Hunt. Look for:

  • Requests with Content-Encoding: br on endpoints accepting bodies — especially bodies that are large or that your app doesn't legitimately expect compressed
  • Responses to node2shell-check-style probes
  • Unexplained Node.js crashes/restarts (OOM-like exits on single requests) — failed exploit attempts crash the process
  • Egress connections or child processes spawning from your Node workers

The bigger picture

Node2Shell was reported by OpenAI's Codex Security team, PHP2Shell's discovery two days earlier was AI-assisted across three research teams, and the first in-the-wild Brotli exploit traffic came from an IP also serving PHP2Shell exploits — one actor, collecting. The pattern across this fortnight is consistent: AI systems are now finding, weaponizing and distributing memory-corruption exploits in core runtime libraries, and the vulnerability-response clock is compressing from months to days. Treat that as your planning assumption.

Pre-auth RCE through a decoder nobody knew they were running is exactly the class of finding our penetration tests surface, the dependency-and-runtime upgrade discipline is what our DevSecOps pipelines automate, and the crash-and-probe hunting above is daily work in our managed SOC. If your Node.js fleet spans versions nobody can account for, talk to us — we'll inventory it and get it patched.

Frequently Asked Questions

My Node app doesn't use Brotli anywhere. Am I safe?

Probably — but verify rather than assume. The decoder ships in the runtime, and two common paths don't require any explicit Brotli code: body parsers that honor Content-Encoding: br (Express 5 / body-parser 2.x do by default) and built-in fetch() decompressing Brotli responses. Grep your dependency tree before concluding you're out of scope.

Is this the same bug as HTTP2Shell?

Yes. HTTP2Shell is the name Aikido gave to the ecosystem-wide reach of the same CVE-2026-111497 — the identical Brotli decoder flaw inside Python packages, Envoy, Istio, .NET and more. Node.js was where it was first demonstrated; the fix matrix is much bigger. See our HTTP2Shell companion post.

Node 20 LTS is still widely used. Really no fix?

Correct — Node.js 20, 23 and 25 are end-of-life and receive no fix for this CVE. The only remediation is moving to a supported line: 22.23.4 LTS, 24.22.1 LTS or 26.12.1. This is the running cost of EOL runtimes, and CISA's 10 November federal deadline is a reminder that KEV timelines do not wait for upgrade roadmaps.

I patched the package but didn't restart. Am I fixed?

No. The fix is in the compiled binary and its bundled Brotli. Until every Node process is restarted — and every container image rebuilt on a patched base — the vulnerable decoder is still running in memory. Check node --version from inside the running process environment, not from your deployment history.

How urgent is this, really?

CISA added it to KEV on 27 October based on evidence of active exploitation, honeypots logged real attempts, public PoCs are on GitHub, and exploit traffic has already been observed from the same infrastructure serving other fresh exploits. Failed attacks still crash processes. This is a this-week patch.


Sources: Node.js October 2026 Security Releases; NVD/Google CNA; Imperva Red Team; ELLIO; CISA KEV (27 Oct 2026); Aikido; Bun; MSRC. Exploit details above are defensive-level and already public.

Get Started

Want a security-first build?

Get a free security review. We'll look at where you stand today and tell you what to fix first, no strings attached.

Talk to an Expert
Node2Shell (CVE-2026-111497) — Brotli Decoder RCE in Node.js, Explained - IKZERO