Node2Shell: Check Your Node.js Version Before November 10

By Andrew Owens |

If your app runs on Node.js, check the version today. A bug called Node2Shell (CVE-2026-111497) lets an attacker send a malformed Brotli-compressed stream that crashes the process, leaks memory, or in some setups runs code on your server. No login needed.

On October 27, CISA added it to the Known Exploited Vulnerabilities catalog, listed as "Google Brotli Unchecked Return Value Vulnerability," with a federal remediation deadline of November 10. That listing means CISA has evidence of real-world exploitation. A week earlier, nobody had reported any.

What the bug is

Node.js ships its own copy of Google's Brotli decompression library inside the runtime binary. According to Akamai's write-up, a structurally invalid block-type switch in a Brotli stream was treated as "need more input" instead of an error, so the decoder kept appending data to an eight-byte internal buffer and wrote past the end of it. That's the out-of-bounds write.

Google, as the CVE numbering authority, scored it 9.2 on CVSS 4.0. The bug has been in Brotli since version 1.0.3, released in March 2018, per ELLIO. Node.js fixed it in its October 21 security release.

Because the bug lives in the runtime, updating an npm package won't fix it. HeroDevs points out that the npm package called "brotli" is unrelated. You need a new Node binary.

How attacker data gets in

You don't have to call a Brotli function yourself to be exposed. The sources agree on three paths:

  • Express 5 request bodies. body-parser 2.x decompresses bodies sent with Content-Encoding: br by default, because its inflate option defaults to true. Any client on the internet can send one.
  • Built-in fetch() responses. fetch() decompresses Brotli responses automatically. If your app fetches URLs you don't control (webhooks, link previews, "import from URL"), a malicious server can reach you through its response.
  • Direct zlib calls. Anything that passes untrusted input to zlib.brotliDecompress() or zlib.createBrotliDecompress().

A bare node:http server doesn't decode incoming Brotli bodies on its own, so the framework and the outbound calls are what matter.

How real is the exploit?

Real enough. A public proof of concept showed up on October 22. It only got code execution against a deliberately weakened build, but Imperva's red team then reported code execution on the official Node.js 24.22.0 Linux arm64 image, through both fetch() and Express 5. Not on every attempt.

ELLIO went further. In its own tests on x86-64, it got a shell against the stock node:24.22.0-slim image, against Express 5.3.0 with express.json(), and against Koa 3. Most of its 400 Express attempts crashed the worker instead of returning a shell, which is still a denial of service. In an update dated October 26, ELLIO said its sensors had recorded attacker traffic carrying exploit code aimed at Node.js 24.22.0. That attacker's chain scored zero hits in 50 attempts when ELLIO replayed it, so this is early, clumsy exploitation. It won't stay clumsy.

The scale is big. Wiz says 91% of the cloud environments it sees have vulnerable resources, and Akamai's telemetry puts affected Node.js versions on 61% of networks. And the same bug hits other software that bundles Brotli, including Bun before 1.4.4, Envoy, Istio, and Python's Brotli packages.

Are you exposed?

Fixed versions are 22.23.4, 24.22.1 and 26.12.1. Anything older on those lines is vulnerable. Node 25, 23, 20 and earlier are end of life and won't get a fix. Node 20 went EOL on April 30, 2026, and Akamai notes the Node advisory lists no workaround.

Check what actually runs in production, not what's on your laptop:

  • Containers. Run docker run --rm <image> node --version against the image you deploy. A Dockerfile pinned to node:24.22.0 or node:22.23.3-alpine stays vulnerable until you change the tag and rebuild, as Aikido points out. A floating tag like node:22-alpine only helps if you've rebuilt since October 21.
  • Serverless. A Lambda function on the nodejs20.x runtime is running Node 20, which is end of life and won't be fixed. Move it to a supported runtime.
  • VMs and CI runners. node --version on each one. Distro packages may link the system Brotli library, so check your distro's advisory too.
  • Express 5. npm ls express body-parser. If you see Express 5 or body-parser 2.x, that service takes compressed bodies from anyone.
  • Outbound fetches. Make a list of every place the app fetches a URL a user or a third party can influence. Those services go to the top of the patch list.

What to do this week

  1. Upgrade within your line. 22.23.4, 24.22.1 or 26.12.1. It's a patch-level bump with no API changes, per HeroDevs.
  2. Rebuild and restart everything. Every image, every function, every compiled binary that embeds the runtime. A patched base image does nothing for a container that's still running the old one.
  3. Get off Node 20. There's no upstream fix coming. If you can't move this week, put it on the calendar for next week and use the stopgaps below in the meantime.
  4. Block Brotli request bodies until you've patched. Reject Content-Encoding: br at your proxy or load balancer, or set body-parser's inflate option to false. This does nothing for fetch() responses, so it's a stopgap, not a fix.
  5. Watch for crashes. A Node worker that keeps segfaulting after restart is worth a look. ELLIO also suggests limiting who can read core dumps, since a crash can write secrets to disk.

November 10 is a deadline for federal agencies, not for you. But it's a useful one to borrow. As we wrote about a few weeks ago, the gap between disclosure and exploitation is now measured in days. This one took less than a week to show up in the wild.

Where a pen test fits

Patching is on you. Nobody outside your team can bump a Dockerfile tag or redeploy a Lambda. What a pen test can do is show you what your stack tells the world: framework fingerprints, headers like X-Powered-By: Express, version strings, and the forgotten staging box still running last year's image. Those are the things attackers scan for first.

The free scan is a quick look at what's visible on your domain. A $495 pen test goes further, with a human reviewing every finding, and it'll flag outdated runtimes and frameworks where they can be seen from outside.

Ready to fortify your defenses against cyber threats?

Start Your Penetration Test Now