DEV Community

RC
RC

Posted on Originally published at randomchaos.us

Node.js patched a code-execution Brotli bug October 21

Imperva's researchers ran attacker-supplied code on an official Node.js 24.22.0 image for Linux arm64, with no debugger and no custom build, by feeding crafted data to the Brotli decoder that Node bundles. The proof of concept, which they call Node2Shell, targets CVE-2026-111497. Node.js disclosed the flaw and shipped fixes on October 21.

The result comes with limits worth stating up front. The exploit did not land on every attempt; code execution took several tries in that arm64 setup. Imperva tested one build and one application configuration, and did not test other architectures or the 22.x and 26.x release lines. On the fixed build, 24.22.1, the same input was rejected and nothing ran.

That still clears a bar the earlier public work did not. The first public proof of concept achieved 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, and said no working attack on a normal build was achieved. Node2Shell reaches code execution on a stock image.

No exploitation in the wild had been reported as of October 22.

How the bug works

A Brotli stream announces the block types it will use as decoding proceeds. Imperva's input reached a block change that the stream's own structure had already ruled out, a transition the format does not allow. The vulnerable decoder treated that as a request for more input and kept going in an invalid state. In a separate run under a memory sanitizer, continuing from that state wrote past the intended buffer. That out-of-bounds write is the flaw. Node fixed it by importing an upstream Brotli patch that makes the decoder stop with a format error at that point rather than carry on.

Which applications are exposed

The decoder is bundled, so every current line carries it: 22.x, 24.x and 26.x. An application touches the decoder from either side of an HTTP exchange. It decodes a Brotli-compressed response when it reads one through Node's built-in fetch(). It decodes a Brotli-compressed request body when middleware does so.

Express 5's body parsers now handle Brotli. express.json(), express.urlencoded(), express.text() and express.raw() decode Brotli request bodies alongside gzip and deflate when applied to a route. A plain Node HTTP server does not decode a Brotli request body just because one arrives; that path needs the middleware or an explicit call. Imperva reproduced code execution through both the request-body and the response paths, against Express 5.0.0 through 5.3.0 and body-parser 2.0.0 through 2.3.0 at pinned versions, not every release in between.

The response path matters because it does not depend on an attacker reaching your request handlers. A service that fetches from an upstream it does not fully control, and lets that upstream return Brotli, is exposed through the same decoder.

What to do

Move to a patched release. The advisory lists no in-runtime workaround, so an application that cannot update has no fallback inside Node itself. The update has to reach everything that bundles a Node binary, including container images and other artifacts that a package inventory can miss. Lines 20.x and earlier are end of life and do not get the fix; those need to move to a supported line regardless.

Where updating has to wait, Imperva suggested limiting untrusted Brotli traffic on the affected paths where that is practical, and was clear that only the update removes the flaw. It added rules to its own web application firewall and tested them against the attacks it reproduced. Vercel, which runs Node.js for its customers, said it added rules on October 21 that block the attack in inbound requests. Those rules do not cover Brotli responses an application fetches from other servers, and they do not replace updating.

Top comments (0)