Malicious Brotli data can make an application on an official build of Node.js run attacker-supplied code, security researchers at Imperva have demonstrated.

Their "Node2Shell" proof of concept ran code on the official Node.js 24.22.0 image for Linux arm64, with no debugger or custom build, by feeding crafted data to the bundled Brotli decoder (CVE-2026-111497). Node.js disclosed the flaw and released fixes on October 21, and applications that decode untrusted Brotli data should update.

Imperva's test did not succeed on every attempt, and code execution took several tries in its Linux arm64 setup. The result covers that one build and application setup, with other architectures and the 22.x and 26.x lines not tested by Imperva. On the fixed release, 24.22.1, the same data was rejected, and no code ran.

The demonstration goes beyond the first public proof of concept, which achieved code execution only on a deliberately weakened test build. On a standard build, that proof of concept's author reported a crash and a limited read of process memory, not code execution, and said a working attack on a normal build was not achieved.

No exploitation of the flaw in the wild was reported as of October 22. By October 26, one crafted exploit had reached the sensors of threat-intelligence firm ELLIO. In the firm's own tests, that attempt did not work.

Which Applications Are Affected

The flaw is in the Brotli decoder that Node.js bundles, so it affects applications on the 22.x, 24.x, and 26.x lines, not only the one build Imperva tested. A service reaches that decoder from either side of an HTTP exchange: when it reads a Brotli-compressed response through Node.js's built-in fetch(), or when middleware decodes a Brotli-compressed request body.

Express 5's body parsers, such as express.json(), express.urlencoded(), express.text() and express.raw(), now decode Brotli request bodies when applied to a route, alongside gzip and deflate. A plain Node.js HTTP server does not decode a Brotli request body just by receiving one, so the decoding needs that middleware or an explicit call.

Imperva reproduced code execution through both the request-body and response paths, testing Express 5.0.0 to 5.3.0 and body-parser 2.0.0 to 2.3.0 at pinned versions, and did not test every release in between.

How the Bug Works

Imperva traced the cause to how the decoder handles an impossible point in a Brotli stream: the stream announces the block types it will use as decoding runs, and the test input reached a block change that the stream's own structure ruled out. The vulnerable decoder read that as a request for more input and kept going in an invalid state.

In a separate test with a memory sanitizer, continuing from that state wrote past an intended buffer, the out-of-bounds write behind the flaw.

Node.js fixed the flaw by importing an upstream Brotli patch that makes the decoder stop with a format error at that point instead of continuing.

What to Do

The fix is to move to a patched release. The update should reach container images and other artifacts that bundle a Node binary, which a package inventory can miss. End-of-life lines 20.x and earlier do not receive the fix and should be moved to a supported line.

The advisory lists no workaround inside Node.js, so an application that cannot update at once has no fallback in the runtime. Imperva suggested limiting untrusted Brotli traffic on affected paths where that is practical, and said only the update removes the flaw. The company also 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. The rules do not cover Brotli responses an application fetches from other servers, and do not replace updating.

Update: Code Execution on x86-64 and First In-the-Wild Traffic

On October 25 ELLIO published its own reproduction of the flaw on x86-64, the architecture Imperva had not tested. It got a shell on the official 24.22.0 binaries under default settings, with no debugger and the standard memory-layout randomization (ASLR) left on, against four setups: a plain node:http server, the official node:24.22.0-slim image running as root, Express 5.3.0, and Koa 3. Each shell ran as the service's own account.

The exploit is tied to one build at a time. An offset that gave a shell on 24.22.0 produced nothing in 10,000 attempts against 22.23.3 and 26.12.0, each of which needs a different offset. That makes the attack version-specific: an operator measures a build once, then reuses the result against every host running it.

Scanning has already begun. Before the exploit traffic, a public exposure checker calling itself node2shell-check reached ELLIO's sensors, sending Brotli bodies to find hosts that decompress Brotli at all.

On October 26 at 15:08 UTC, a crafted request body built to attack Node.js 24.22.0 on x86-64 reached those sensors, from an address ELLIO rates malicious, 94.154.43.7. Its delivery copied public proof-of-concept code, but the part that takes over the process was custom, not copied. In ELLIO's tests it failed, getting no shell in 50 tries, where ELLIO's own chain worked against the same target.

ELLIO read the attempt as the flaw being picked up by an operator aiming at mass exploitation, and said to treat it as exploited and patch now. No successful compromise had been reported as of October 27.

(The story was updated after publication on October 28, 2026, to include ELLIO's reproduction of code execution on x86-64 across four application setups, its finding that each build needs its own memory offset, and the first scanning and exploit traffic observed in the wild.)

Found this article interesting? Follow us on Google News, Twitter and LinkedIn to read more exclusive content we post.