· 6 min read

Follow-up: A Working Node.js Brotli Exploit Now Runs on the Official Node 24.22.0 Image. Check Your Docker Tags, Not Just Your Laptop.

Yesterday's post on the October 21 Node.js Brotli fix said the public proof of concept only got code execution on a weakened test build. That is no longer the whole story. Imperva researchers demonstrated an exploit they call Node2Shell that reaches code execution on the official Node.js 24.22.0 Linux arm64 image, with no debugger and no custom build, according to The Hacker News. It did not work every time and took several attempts. It worked.

What is new

The flaw is CVE-2026-111497, an out-of-bounds write in the Brotli decoder bundled with Node. It comes from the decoder mishandling an impossible block-type change in a compressed stream: it treats the change as a request for more input and keeps decoding in an invalid state. Imperva confirmed the write with a memory sanitizer.

The earlier public proof of concept produced a crash and a limited memory read on a standard build. Imperva's work is the step beyond that. The Hacker News reported no exploitation in the wild in the coverage I read, but a reliable-enough demonstration on a stock image is the point where I stop treating this as something to patch next week.

What Imperva tested, and what it did not

Be precise here, because the claims drift in retelling. Imperva tested the official 24.22.0 arm64 image only. The 22.x and 26.x lines are listed as affected, but other architectures and those lines were not tested by Imperva.

Two application patterns expose you. One is reading Brotli-compressed responses through Node's built-in fetch(). The other is decoding Brotli request bodies through middleware. Imperva tested Express 5.0.0 through 5.3.0 and body-parser 2.0.0 through 2.3.0, not every release in between. A plain Node HTTP server does not decode Brotli request bodies by default, so a minimal API with no compression middleware is less exposed on the inbound path.

The outbound path is easy to miss. If your server calls a third-party API with fetch() and that response can be influenced by someone else, that decoder runs on their bytes.

The container gap

Here is why I am writing a follow-up instead of repeating yesterday's advice. Updating Node on your laptop does nothing for the Node binary inside your Docker image. Most solo deploys are a Dockerfile with a tag like node:24 or a pinned patch version that nobody has touched in months, built once, deployed many times.

Node released fixes on October 21 by importing an upstream Brotli patch, and 24.22.1 rejects the same data Imperva used. Versions 20.x and earlier are end of life and get no fix, so if that is your tag, upgrading the major version is the fix.

Imperva added WAF rules, and Vercel added inbound request rules on October 21, but neither covers Brotli responses your server fetches from other servers, and neither replaces updating. No runtime workaround is documented, so only updating removes the flaw.

Fifteen minutes on a Wednesday

Find every place a Node binary lives. That list usually includes Dockerfiles, CI images, serverless function runtimes you pin yourself, and any VPS where you installed Node by hand. Run node --version in each running container, not in the repository. Anything below 22.23.4, 24.22.1 or 26.12.1 on its line needs a rebuild.

Rebuild with --pull so you do not reuse a cached base layer, redeploy, and check the version again from inside the live container. If you are on 20.x or older, put the major-version upgrade on this week's list rather than next quarter's.

Then check for the two exposure patterns in your own code: search for fetch( calls to services you do not control, and for Brotli or compression middleware on request bodies. If you find neither, your practical risk is lower, but I would still patch, because dependencies inside your tree may use both.

Where this could be wrong

Imperva's exploit was unreliable and tested on one image. A real attacker needs to deliver a crafted stream to a vulnerable decoder, and your server has to be one that decodes it. For many small apps that combination will not exist. If your server never uses fetch() on untrusted responses and never decodes request bodies, the chance anyone reaches this bug on you is small. I would still patch, because the fix costs less than finding out which of the two cases you are in.

What I would actually do

Rebuild every Node container today and pin the patch version in the Dockerfile so you can see the number in your diff. Then put a recurring monthly rebuild on the calendar. This bug is one instance of a pattern: the code you wrote is rarely the code that gets exploited.

Author

Sources

Newsletter

The week, in one email.

The weekly summary of what happened and what it means for people who build alone. No spam, unsubscribe any time.

Form not showing? Subscribe on this page instead.

Related Posts