Is Your Node.js Version Vulnerable to the Brotli RCE? Check in 2 Minutes

2026-11-05来源 dev.to SecurityEnglish
Is Your Node.js Version Vulnerable to the Brotli RCE? Check in 2 Minutes

Run this now. If your version sits below the floor in the table, the Node.js Brotli RCE (CVE-2026-111497) applies to you, and attackers are already using it.

Try Apidog today

node -v 
Release line Vulnerable Fixed from Latest (Nov 5, 2026)
26.x Below 26.12.1 26.12.1 26.13.0
24.x Below 24.22.1 24.22.1 24.22.2
22.x Below 22.23.4 22.23.4 22.23.4
25.x, 20.x, older End-of-Life: unpatched and unassessed No fix exists Move to 24 or 26

For a second signal, ask the binary directly:

node -p "require('node:zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH" 

The fix added this constant. We ran it on nine official macOS builds from the three supported lines: every fixed build printed -17, and every older build printed undefined. Trust node -v first, because a distro that backports the patch may not add the constant.

Skip process.versions.brotli. It printed 1.2.0 on both 24.21.0 and 24.22.1, and 1.1.0 on both 22.23.3 and 22.23.4. Node.js cherry-picked the fix without changing the reported version, so this field can’t tell you whether you’re patched.

Apidog CLI: let your coding agent run your API test suites before and after this upgrade — get started here.

TL;DR

Question Answer
What is it? Out-of-bounds write in the Brotli decoder bundled with Node.js, also called Node2Shell or HTTP2Shell
Impact Crash, memory disclosure, or remote code execution
Severity HIGH per Node.js; 9.8 CRITICAL (CVSS 3.1) per NVD
Exploited? Yes. CISA KEV listed it on October 27, 2026
Login needed? No, per NVD’s vector and Akamai’s analysis
Who’s reachable? Apps that decode Brotli they don’t control: Express 5 bodies, fetch() responses, direct node:zlib calls
Fix Upgrade the runtime, rebuild images, restart processes
Workaround? The advisory lists none. Interim measures below reduce exposure but don’t cover everything

Timeline of CVE-2026-111497: Node.js fixes on October 21, CVE published October 22, exploit traffic reported October 26, CISA KEV listing October 27, KEV due date November 10, next Node.js security release November 11

ELLIO reported exploit traffic in its sensors by October 26, five days after the fix. The payload it captured targeted Node.js 24.22.0 on x86-64. That payload’s final stage failed in ELLIO’s tests, but ELLIO also confirmed code execution itself on stock 24.22.0 builds. CISA gave US federal agencies until November 10 to act, and it marks ransomware use as "Unknown". Akamai’s telemetry showed 61% of networks running affected versions on October 26.

Node2Shell explained: what the Brotli bug does

Node2Shell is an out-of-bounds write in the Brotli decoder that ships inside the Node.js runtime. A malformed compressed stream makes the decoder write past the end of an eight-byte internal buffer, which corrupts process memory. The result can be a crash, leaked memory, or attacker-controlled code running on your server.

The root cause is a misclassified error. An invalid block switch came back as "I need more input" instead of "this data is broken", so the streaming decoder kept appending bytes to that small buffer.

The bug lives in Google’s Brotli library, versions 1.0.3 through 1.2.0, so it isn’t only a Node.js problem. Node.js fixed its copy by cherry-picking upstream commit 0ed4268a into the 24.22.1 release and its siblings. OpenAI Codex Security researchers Khai Tran and Sylvie Mayer reported it.

Your dependency scanner won’t catch this, because the decoder ships in the runtime, not a package. npm install reported 0 vulnerabilities for our Express 5 test project while running on an unpatched Node.js 24.15.0.

Who is reachable

A vulnerable runtime is half the condition. The other half is a path that feeds untrusted Brotli data to the decoder. We tested each path on Node.js 24.22.1 by sending a harmless invalid body (the text not-valid-brotli) with Content-Encoding: br, and by serving valid Brotli to each client.

Path Version tested Runs the decoder? What we saw
Express 5 express.json() 5.3.0 (body-parser 2.4.0) Yes, by default 400, “Decompression failed”
Express 4 express.json() 4.22.3 (body-parser 1.20.9) No 415, unsupported encoding
Koa @koa/bodyparser 6.1.0 Yes, by default 500, BrotliDecoder error in the log
Fastify with @fastify/compress 9.3.0 Yes, once registered 400, FST_CP_ERR_INVALID_CONTENT
Plain Fastify 5.12.8 No 400, invalid JSON
Bare node:http server or http.get 24.22.1 No Raw bytes pass through
Built-in fetch() 24.22.1 Yes, always Decoded the response
axios, got, node-fetch 1.21.0, 16.2.0, 3.3.2 Yes, by default Decoded the response
zlib.brotliDecompress*, createBrotliDecompress Any Yes This is the decoder API

Diagram of paths into the Brotli decoder in node:zlib: Express 5 parsers, Koa body parsers and Fastify with @fastify/compress on the inbound side, fetch, axios and got on the outbound side, while Express 4, plain Fastify and bare node:http do not reach it

Two results deserve a second look. fetch() decoded Brotli even when the request sent Accept-Encoding: identity, because it follows the response header, not what you asked for. Any server your app calls can reach the decoder, so a webhook tester, link previewer or URL importer is exposed. And a 400 alone proves nothing: Express 5 and plain Fastify both return one, but only Express 5 ran the decoder. Read the error message.

Find your paths with three commands:

# Direct decoder use grep -rnE "brotliDecompress|createBrotliDecompress" src/ # Parsers that decode request bodies npm ls express body-parser @koa/bodyparser @fastify/compress # Base images and pinned versions grep -rnE "^FROM .*node" --include='Dockerfile*' . 

One new fetch() call changes these answers, so “not reachable” still means patch.

How to patch CVE-2026-111497

Go to the latest build on your line, not the floor. These sources all carried fixed builds on November 5:

Install method Do this
nvm nvm install 24 && nvm alias default 24
Homebrew brew upgrade node (or node@24, node@22)
Docker FROM node:24.22.2-slim, then docker build --pull
Linux distro packages Follow your distro’s advisory, since it may backport the fix under an older version number
CI Update .nvmrc, engines and node-version, then rebuild
Serverless Log process.version from a deployed function; don’t assume the platform updated it

Then close the two gaps that catch people out:

  • Restart everything. A running process keeps the old binary in memory, so upgrading on disk changes nothing until pm2, systemd or your orchestrator restarts it.
  • Rebuild what you shipped. Old images keep the old runtime, and images pinned by digest need an image scan because the tag won’t tell you.

Finish by running the checks at the top of this post inside the deployed container, then on CI runners and developer machines. To see the newest security build per line:

curl -s https://nodejs.org/dist/index.json | jq -r '[.[] | select(.security == true and (.version | test("^v(22|24|26)\\.")))] | group_by(.version | split(".")[0]) | .[] | max_by(.date) | "\(.version) \(.date)"' 

Still on 20 or 25? No build exists for you. Read the Node.js 20 End-of-Life plan and move.

Node.js Brotli mitigations if you can’t patch today

These buy time. None replaces the upgrade.

  1. Reject br at the edge. Block Content-Encoding: br at your proxy or load balancer, matching case-insensitively and inside lists like gzip, br.
  2. Reject it in the app. Put this before your body parsers. It returned 415 in our Express 5 test:
 app.use((req, res, next) => { const encoding = req.headers['content-encoding'] || ''; if (/\bbr\b/i.test(encoding)) return res.status(415).end(); next(); }); 
  1. Turn off inflation. express.json({ inflate: false }) returned 415 for the same request. It rejects gzip bodies too, so check your clients first.
  2. Control outbound responses. axios accepts decompress: false, which left the bytes undecoded in our test. fetch() has no standard off switch, so restrict which hosts it can reach with an allowlist and egress rules.
  3. Limit the blast radius. Segment Node.js services, and restrict who can read core dumps, since ELLIO notes crashes can expose in-memory data there.

Watch for what ELLIO saw first: bursts of Brotli-encoded request bodies mapping which hosts decode them. Unexpected Content-Encoding: br traffic on a JSON API, or a spike in worker crashes, is worth an investigation.

Prove your API survived the upgrade: let your agent run it

A runtime jump can change TLS, DNS and HTTP behavior, and October’s release touched all three. Don’t diff that by hand. Let your coding agent run Apidog CLI before and after, then report what moved.

npm install -g apidog-cli@latest apidog login --with-token <YOUR_TOKEN> npx -y skills add https://apidog.com # pick your agent when prompted 

Give your agent one [REDACTED PROMPT]
Run Apidog test suite in project against staging and save the
results to reports/before. After I upgrade Node.js, add a test case for
POST /orders that sends the plain text "not-valid-brotli" with
Content-Encoding: br, asserts a 4xx status, then checks GET /health
returns 200. Rerun the suite to reports/after and list every request
whose result changed.

 The agent runs these commands: 


bash
apidog run --test-suite --project -e -r cli,junit --out-dir reports/before
apidog cli-schema get test-case-create
apidog cli-schema validate test-case-create --file case.json
apidog test-case create --project --file case.json
apidog run --test-case --project -e -r cli,json
apidog run --test-suite --project -e -r cli,junit --out-dir reports/after

 That extra case is a health check, not an exploit: the body is plain text, and it failed with the same padding error on vulnerable and fixed builds in our test. It catches a real gap, though. Our Koa test answered 500 because the decode error went unhandled, and a public API should answer 4xx. Run it against staging only. The [run command reference](http://apidog.com/blog/apidog-run-command-reference?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation) lists every flag, and the [CI/CD guide](http://apidog.com/blog/apidog-cli-ci-cd-pipeline?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation) puts the same JUnit run in your pipeline. Apidog’s Free plan covers up to 4 users. ## Why November 11 matters next The Node.js project has [pre-announced another release](https://nodejs.org/en/blog/vulnerability/november-2026-security-releases) for 26.x, 24.x and 22.x on or shortly after November 11, 2026, again with HIGH as the top severity. No CVEs are public yet. [Our November release guide](http://apidog.com/blog/nodejs-november-2026-security-release?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation) covers how to prepare. Patch Brotli now rather than folding it into that window. The bug has public details and live traffic today, and the upgrade you run this week rehearses the next one: same inventory, same image rebuilds, same test suite. [PHP2Shell](http://apidog.com/blog/php2shell-php-vulnerability?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation) and [React2Shell](http://apidog.com/blog/fix-react2shell-exploit?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation) moved the same way. ## FAQ **Is CVE-2026-111497 being exploited?** Yes. CISA added it to the Known Exploited Vulnerabilities catalog on October 27, 2026, under Google Brotli, and ELLIO recorded exploit traffic by October 26. **Is it high or critical?** Both labels are real. Node.js rates it HIGH. NVD scores it 9.8 CRITICAL on CVSS 3.1, and the Google-issued CVSS 4.0 score is 9.2. Plan around the fact that it’s exploited. **I’m on Express 4. Am I safe?** Your request bodies don’t reach the decoder, but `fetch()`, axios and direct `node:zlib` calls still do. The same release also fixed nine other CVEs, so upgrade anyway. **Is Node.js 20 affected?** The project doesn’t assess End-of-Life lines, and they get no fix. Treat 20.x and 25.x as unpatched and unassessed, then migrate. **Does this affect anything besides Node.js?** Yes. The [GitHub advisory](https://github.com/advisories/GHSA-9v8h-qqgm-4m35) covers the PyPI `brotli` and `brotlicffi` packages, and vendors report fixes for Bun 1.4.4 and .NET. Update each decoder separately, because a Node.js upgrade only covers its own copy. 

原文:https://www.a8q9.cn/en/article/is-your-node-js-version-vulnerable-to-the-brotli-rce-check-in-2-minutes-ad6901.html

相关文章