Skip to content
Threat landscape

Node2Shell: why patching Node alone is not enough

Node2Shell exploits a flaw in the Brotli decoder that Node.js bundles. The fix ships in Node 22.23.4, 24.22.1 and 26.12.1 – and in Brotli itself.

  • by blacklens.io Team
  • Published
  • 5 min read
Brightly coloured lines of source code on a dark screen, photographed at an angle

Node.js published security updates for ten vulnerabilities on 21 October 2026. The most severe, CVE-2026-111497, allows remote code execution through crafted Brotli data. But the faulty code is not in Node.js. It is in the compression library Node.js bundles – and that decides whether an updated Node version closes the hole at all.

What Node2Shell is

Node2Shell is the name Imperva gave its 22 October analysis of CVE-2026-111497: an out-of-bounds write in the Brotli decoder that Node.js bundles in its 22.x, 24.x and 26.x lines. Node.js rates the flaw "high" and fixed it on 21 October. The CVE record is harsher: CVSS 9.2 under version 4.0, severity critical. Khai Tran and Sylvie Mayer of OpenAI Codex Security reported it; two members of the Node.js team wrote the fix.

The flaw landed on the same day as PHP2Shell, with the same score. Who reported them is worth noting: OpenAI researchers were among the reporters at PHP, and here it is that company's Codex Security team. The advisories do not say how much of this came from AI tooling – but AI vendors' security teams appearing in the acknowledgements has been routine since April.

The bug is in Brotli, not in Node.js

The vulnerable code sits in the reference implementation of Brotli, Google's compression format, in c/dec/decode.c. While decompressing, the decoder checks whether a stream switches to a different block type. When that check reported an error, the responsible function did not pass it on along the fast decoding path: its callers had no return value for it. The decoder carried on with invalid internal state and wrote past its intended buffer. The CVE record files this as CWE-252, unchecked return value. The fix makes those functions return a format error and aborts the stream.

What matters for the reach is the version range in the CVE record: Brotli releases 1.0.3 through 1.2.0 are affected "on all platforms", and what needs updating is "any Brotli libraries or binaries". The fixed releases are 1.0.10, 1.1.1 and 1.2.1, all published on 21 October. Node.js is the best-known consumer of that library, not the only one.

The decoder is reachable without Brotli in your code

An application does not have to call Brotli anywhere to reach the decoder. Imperva names three routes in: a direct call through node:zlib, Node's built-in fetch(), which decompresses a Brotli response by itself, and the request parsers in Express 5.

The third route widens the attack surface most. The Express 5 migration guide lists Brotli decompression as an improvement that needs no migration step: express.json(), express.urlencoded(), express.text() and express.raw() accept Content-Encoding: br. body-parser documents that all parsers support "automatic inflation" of gzip, br, deflate and zstd; the only option documented for it is inflate, default true. No switch for Brotli alone is documented. An application using express.json() that does not turn inflate off therefore accepts Brotli bodies without that being visible in its own code. A bare HTTP server in Node, by contrast, does not decode incoming Brotli bodies merely by receiving them.

Imperva took this route to code execution: in the official Node image 24.22.0 for Linux/arm64, a Brotli message created a file the test application had no code to create – with no debugger, but not on every attempt. 24.22.1 rejected the same inputs. That is the only public demonstration so far. Neither Node.js nor Imperva reports attacks in the wild, and as of 23 October the flaw is not in the CISA KEV catalogue.

Why a patched Node version is not enough

Whether node -v shows a corrected release settles the question only where Node uses its own Brotli code. Debian's package does not. Its security tracker notes on CVE-2026-111497 that nodejs uses the system-provided Brotli library, and lists both packages accordingly: brotli in Bookworm, Trixie and Sid as "vulnerable", with "(unfixed)" as the fixed version, and nodejs at urgency "unimportant". On those systems only the Brotli package closes the hole, and on 23 October it was not available there.

ComponentFixed releaseNote
Node.js 26.x26.12.1current line
Node.js 24.x24.22.1current LTS
Node.js 22.x22.23.4LTS
Node.js 20.x and olderno fixend of life, always affected per the advisory
Brotli library1.0.10, 1.1.1, 1.2.1decisive where Node uses the system library

What to do now

These steps differ from an ordinary update in one respect: the Node version alone does not decide the exposure.

  • Update the runtimes and restart the processes afterwards. A running process keeps the old decoder in memory.
  • Check the Brotli library as well wherever Node comes from a distribution package. There, libbrotli is the binding version, not Node's.
  • Check every image individually, not just the host. Version numbers go stale fastest in container images and appliances with a built-in runtime; a host package list does not show them.
  • Limit Brotli traffic from unknown sources while the update waits. inflate: false rejects gzip and deflate too and may break existing clients; a limit on the affected paths, at the web server or in the web application firewall, is the narrower cut.
  • Do not lose sight of the other nine flaws. The 21 October release fixes ten CVEs, three more of them rated "high": truncated DNS names containing a NUL character, and two bypasses of the TLS hostname check.
  • Do not read decoder errors as proof. Crashed processes and decoder errors on their own are not a reliable sign of an attack, as Imperva points out. They carry weight only together with the release actually deployed and the origin of the compressed data.

What helps – and why visibility decides

The question on the day of an update like this is not how to update Node.js, but where a runtime is running at all. Through Attack Surface Management, blacklens.io continuously discovers from the outside which services and web applications are reachable and which technologies, in which versions, run on them; internal systems are covered by the Sentry Agent. The Threat Center matches new CVEs against that inventory in real time and shows whether a hit rests on the exact version or only on the product.

One limit belongs in the picture: which Brotli library a Node process has loaded is not something any service reveals from the outside. That check stays on the system – which systems it applies to can be answered beforehand.

Frequently asked questions

Node2Shell is the name the security firm Imperva gave its analysis of CVE-2026-111497, an out-of-bounds write in the Brotli decoder that Node.js bundles. An attacker who can send crafted Brotli data to an application that decompresses it can overwrite memory and, in the end, run their own code. Node.js fixed the flaw on 21 October 2026; the CVE record rates it CVSS 9.2 under version 4.0.

Share

More articles

See your attack surface within 24 hours.

We set up your workspace and walk you through the first findings.

  • 14 days free, all features
  • No credit card required
  • We walk you through your first findings