Node.js disclosed CVE-2026-111497 on October 21, 2026: a high-severity out-of-bounds write in the Brotli decoder that ships inside the runtime. The project says the flaw permits remote code execution and fixed it in Node.js 22.23.4, 24.22.1, and 26.12.1. A day later, Imperva's red team ran attacker-supplied code on the official Node.js 24.22.0 image for Linux arm64. It named the exploit Node2Shell, and most coverage now uses that name for the bug itself.
No exploitation in the wild has been reported as of October 22. Security teams should find every Node.js runtime they run, including the copies inside container images and single executable apps, and move each one to a fixed release. Patch first the services that accept Brotli-compressed request bodies or fetch content from servers they do not control. Node.js 20 and older are end of life and will not get a fix, and Node.js has published no workaround.
Technical overview
The Node.js advisory gives a title, a rating, and the affected lines. The CVE record is reserved, with no CVSS score or CWE published yet, so the mechanics below come from the upstream fix and the researchers who tested it.
The upstream patch was public for roughly eight months before it shipped as a security fix. Anyone diffing Brotli's history had the code change long before the advisory explained why it mattered.
How the bug works
The decoder accepted a block switch that the stream's own header made impossible, then kept decoding from a state it should never reach. Brotli splits literals, commands, and distances into blocks, and the stream declares up front how many block types each category uses. When a block runs out, the decoder reads a switch to the next type. A category with only one declared type should never switch.
In the vulnerable code, the function that reads a switch, DecodeBlockTypeAndLength, returned the same false value for two different conditions: input ran out mid-read, or the stream asked to switch in a category with one type. The callers could not tell them apart. On the streaming path they treated false as a request for more input. On the fast path, used when the decoder knows enough input is buffered, the switch functions returned nothing and the result was ignored. Either way, decoding continued with block state the header ruled out.
Imperva's sanitizer run showed that continuing from that state writes past an intended buffer. The upstream fix gives the impossible switch its own format error, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (code -17), and makes every caller stop on it. Node.js cherry-picked that commit and exposes the new constant in its zlib bindings.
Per a Bun issue that compared runtimes against Node.js's regression vector, an unpatched decoder fails that input later with a block-length error (-9), while a patched one rejects it early with the block-switch error (-17). A block-switch error in the logs of a patched service suggests someone sent it a malformed stream of this shape.
What is proven and what is still open
Code execution on a stock Node.js build is now demonstrated for one build and two application setups. Confirmed by Node.js: the out-of-bounds write exists, it can permit remote code execution, it affects 22.x, 24.x, and 26.x, and the three fixed releases close it.
The first public proof of concept, published on October 22, reached code execution only on a deliberately weakened test build. On a standard build, its author reported a crash and a limited read of process memory, according to The Hacker News.
Imperva's Node2Shell result closed that gap. In fresh containers running the official Node.js 24.22.0 Linux arm64 image, with no debugger or instrumented binary, crafted Brotli data made a test application create a file it had no code to create. It worked through a response consumed by the built-in fetch() and through Express 5 and body-parser 2 request parsers. It did not succeed on every attempt. The same inputs on 24.22.1 were rejected, and the server stayed up.
Still open: Imperva did not test x86-64, the 22.x or 26.x lines, or every Express and body-parser release in between. Vercel's bulletin says researchers demonstrated memory disclosure and command execution on unpatched 22.x, 24.x, and 26.x, but does not name the research. Until more work is public, treat every affected line and architecture as exploitable.
Same Node.js version, different risk. The vulnerable code only runs when something decompresses Brotli. Two services on the same Node.js version can face very different real-world risk, depending on whether either one decodes Brotli data from outside its trust boundary. The next section covers how that happens.
Which applications reach the decoder
Node.js does not decode Brotli on its own; an application is exposed when its code or a library decompresses Brotli data an attacker controls. That data can arrive on either side of an HTTP exchange.
A bare http.createServer() does not decode a Brotli body just by receiving it. Express 4 and body-parser 1.x do not decode Brotli request bodies either, since Brotli support in body-parser began with 2.0.0. On the response side, Konfirmity's reading of the bundled undici source is that fetch() advertises Brotli to HTTPS servers by default, so most outbound calls can receive it.
The flaw also reaches beyond the official Node.js binaries:
- Bun compiled the same pre-fix decoder, reachable from fetch(), node:zlib, and DecompressionStream("brotli"), per issue 46352. Bun 1.4.4, released October 22, lists Brotli among its security fixes.
- Distribution packages of Node.js are often built against the system Brotli library. For those, the fix arrives through the distribution's brotli package, so check both.
- Other software that bundles Brotli needs the upstream 1.2.1, 1.1.1, or 1.0.10 release, or the same cherry-picked commit.
Why the outbound path and end-of-life lines matter
The response path is the one perimeter controls miss. Vercel's and Imperva's WAF rules inspect traffic coming in; a payload delivered in the response to a call your service makes never crosses them. Any feature that fetches a URL a user supplies, such as webhook delivery, link previews, image proxies, or import-from-URL, lets an attacker point your server at a host they run and serve the crafted response from there. Vercel notes that self-hosted Next.js image optimization does not block Brotli responses, so remote image patterns that include hosts an attacker can influence are in scope.
The same applies to services that never accept outside traffic. An internal worker that calls a partner API or an AI provider still decodes whatever comes back.
End-of-life lines are the other gap. Node.js only fixes supported lines and states that end-of-life versions are always affected when a security release ships.
Release status per the Node.js release schedule, as of October 23, 2026. Node.js 20 left support less than six months ago, so expect to find it in CI images and long-lived containers. According to W3Techs, Node.js is the visible web server on 7.0% of sites whose server it can identify (September 2026), and that figure misses the many Node.js apps that sit behind nginx, a load balancer, or a CDN.
Verification and detection
Hadrian Atlas customers can start the inventory today. Atlas continuously maps your external attack surface, including subsidiaries and forgotten hosts, and identifies the technologies running on each asset, so internet-facing Node.js and Express services can be listed now.
Hadrian's offensive security team is reviewing the upstream fix and the public research to add checks for this exposure. Atlas, Hadrian's external exposure management solution, can then test whether a flagged asset accepts Brotli-encoded request bodies on routes that reach the decoder, which separates the services that need an emergency upgrade from those that can wait for the next change window.
The outbound path needs a different kind of test, because a service only reaches the decoder when someone points it at a malicious server. Nova, Hadrian's agentic pentesting solution, works through an application the way an attacker would and can show which features, such as webhooks and link previews, will fetch from a host the tester controls.
We will update this post as more research and detection coverage become available.
Recommendations
Updating Node.js is the only fix. Prioritize services that decode Brotli from outside their trust boundary, then cover the rest on a normal rebuild cycle.
- Update Node.js. Move to 22.23.4, 24.22.1, or 26.12.1, or any later release in the same line, and restart every Node.js process. A running process keeps the old decoder until it restarts.
- Rebuild what bundles a runtime. Container images, single executable applications, and startup snapshots each carry their own copy of Node.js. An image pinned to a tag such as node:24.22.0 stays vulnerable however often you rebuild it. The official node images were updated on October 21 and AWS's Lambda base images on October 22. Check with node -p process.version inside each image, since a package inventory can miss a bundled runtime.
- Check managed platforms. As of October 22, Konfirmity reported that AWS Elastic Beanstalk platforms and Amazon Linux 2023 nodejs packages had not yet shipped the fixed versions, and that Lambda's managed runtimes roll out by region. Log process.version from a function to confirm.
- Start with the riskiest services. First, anything that accepts Content-Encoding: br bodies through Express 5 or body-parser 2.x. Second, anything that fetches URLs users supply. Third, code that passes untrusted data to node:zlib Brotli functions.
- Move off end-of-life lines. Node.js 20, 21, 23, and 25 get no fix. Upgrade to 22 or 24.
- Update adjacent runtimes and libraries. Bun users should move to 1.4.4 or later. Distribution-packaged Node.js may also need the distribution's brotli update, and other software that bundles Brotli needs 1.2.1, 1.1.1, or 1.0.10.
If you cannot patch a service right away, reject request bodies with Content-Encoding: br at the reverse proxy, load balancer, or WAF, and keep vendor rules such as Vercel's and Imperva's enabled. Neither step protects Brotli responses your application fetches. Unexpected process exits during decompression and Brotli decoder errors can help prioritize investigation, but neither is a reliable sign of exploitation on its own; correlate them with the runtime version and the source of the compressed data.




