Blog
Node2Shell / Brotli CVE-2026-111497: runtime triage beyond the PHP2Shell headline
CVE-2026-111497 is an out-of-bounds write in the Google Brotli decoder bundled with Node.js and other stacks. Fixed Node trains: 22.23.4 / 24.22.1 / 26.12.1. Operator triage for fetch clients, Express 5 body inflate, and EOL runtimes — without inventing RCE.
KEV · Runtimes
On 21 Oct 2026, the Node.js October 2026 security releases fixed CVE-2026-111497: an out-of-bounds write in the Brotli decoder bundled with Node. Industry nicknames include Node2Shell, HTTP2Shell, and Brotli2Shell. NVD describes remote unauthenticated impact when a process decompresses a crafted Brotli stream; Red Hat RHSB-2026-015 (26 Oct 2026) frames the same decoder flaw across Node, browsers, and anything that links vulnerable brotli and accepts untrusted compressed input.
This is not PHP2Shell. Same news week, different runtime, different inventory problem.
Use the Node.js advisory and vendor bulletins for fixed builds. This post is engagement sequencing.
Snapshot
| Field | Value |
|---|---|
| CVE | CVE-2026-111497 |
| Class | Out-of-bounds write in Brotli decoder (invalid block switch treated as “need more input”) |
| Node severity | High (Node.js advisory) |
| Attack position | Attacker supplies Brotli data the app decompresses (no auth required for that path) |
| Node fixed builds | 22.23.4, 24.22.1, 26.12.1 |
| Upstream Brotli fixed | 1.0.10, 1.1.1, 1.2.1 (affected upstream spans include 1.0.3–1.2.0 — verify per vendor) |
| EOL Node lines | Discontinued lines (e.g. 20.x / 23.x / 25.x) do not get a fix — Node states EOL is always affected when a security release ships |
| Common exposure cues | zlib.brotliDecompress / createBrotliDecompress; built-in fetch() auto-decompress of br responses; Express 5 / body-parser 2 request-body inflate when Content-Encoding: br |
| Not a complete fix | Updating the system brotli package while leaving a bundled Node/runtime copy unpatched |
Bare http.createServer does not auto-decode inbound Brotli bodies — decoding is an application/middleware choice. That distinction matters when you write findings.
Why this is a mid-engagement inventory problem
Node version sprawl is real: nvm, containers, CI images, serverless runtimes, and “we’re on LTS” statements that mean three different patch levels. On authorized testing:
- Finding
nodeon PATH ≠ knowing which binary serves the internet-facing app - A patched host OS Brotli library does not patch the decoder inside the Node binary
- Client-side
fetch()to an untrusted URL is a different path than “someone POSTedContent-Encoding: brto Express”
Industry noise says “Node RCE.” Engagement state asks: which process, which build, which decompress path is in ROE?
Triage questions
- In ROE? App / API / Node runtimes authorized to test (including outbound
fetchSSRF-shaped clients if that is in scope)? - Which Node train?
node -von the serving image/process — not the laptop you used to SSH in. - Does anything decompress untrusted Brotli? Explicit zlib calls,
fetchto attacker-influenced URLs, or middleware that inflatesbrbodies? - EOL line? Node 20.x (and other discontinued lines) will not receive 111497 fixes — upgrade the train, do not wait for a backport that will not arrive.
- What do you pause? Unfinished AD/web paths with evidence deserve a clean pause note — do not abandon them because another “2Shell” headline landed.
Operator path
Loading diagram…
Evidence worth capturing
- Exact Node version string for the serving process (and image digest if containerized)
- Whether the app uses Express 5 / body-parser 2 with default inflate, or explicit Brotli decode APIs
- For client paths: which URLs
fetch(or equivalent) can be influenced to returnContent-Encoding: br - Proof you are talking about the bundled decoder vs a system library — package managers lie across layers
- After upgrade: confirm the process was restarted / image rebuilt (stale workers keep old binaries)
Client-facing remediations (short)
- Upgrade Node to 22.23.4+ / 24.22.1+ / 26.12.1+ (or move off EOL lines onto a fixed train)
- Rebuild and redeploy container images; restart long-lived workers
- Inventory non-Node Brotli consumers (Python brotli/brotlicffi, .NET, native services accepting
br) against vendor fixed versions - Temporary hardening is incomplete: blocking inbound
brdoes not close the outbound fetch → malicious compressed response path
What not to write
- Equating this with PHP2Shell / “all *2Shell bugs are the same”
- “Unauthenticated RCE on every Node box” without a decompress path in evidence
- Treating a system
brotlipackage update as proof the Node runtime is fixed - Completeness claims (“all Node services patched”) without process/image inventory
- Invented in-the-wild exploitation counts — as of late Oct 2026 coverage, treat active exploitation as unconfirmed unless a primary source says otherwise
Primary sources: Node.js October 2026 security releases, NVD CVE-2026-111497, and Red Hat RHSB-2026-015. Re-verify fixed versions there before you file.