CVE-2026-111497 — the Brotli decoder out-of-bounds write in Node.js, Bun, Envoy, .NET and Python.
A single-file detection-only scanner and a realistic lab — by RedSide Security.
No install, no dependencies — just Python 3.8+:
git clone
python3 node2shell/scanner/n2s_scan.py -u --probe
RedSide Node2Shell scanner v1.0.0 - CVE-2026-111497 (detection only)
[VULNERABLE]
evidence : Express via header; Node.js 24.22.0 via /health; probe:decodes (br=200 unknown=415)
*(a "Fixed versions:" reference table also prints after the results)*
- `-u URL` one target · `-l file` a list · `--probe` add the behavioral Brotli check · `--json` / `-o out.csv`
- Exit code **2** if anything is `VULNERABLE` or `LIKELY VULNERABLE` — drop it straight into CI.
- **Detection only.** It never sends the malformed stream that triggers the bug.
> Scan only systems you are authorized to test. Full flag reference: **[scanner/README.md](scanner/README.md)**.
**Node2Shell** (also *HTTP2Shell* / *Broccoli2Shell*, **CVE-2026-111497**) is a critical
out-of-bounds write in Google's Brotli **decoder**, which Node.js, Bun, Envoy, .NET and Python's
Brotli bindings all bundle. A server is exposed when it *decompresses attacker-controlled Brotli
data* — most commonly an HTTP request body sent with `Content-Encoding: br` (Express 5 +
body-parser 2 does this by default). The impact ranges from crash to memory disclosure to remote
This repository is a complete, **honest** toolkit for it:
| **`scanner/`** | A single-file, dependency-free Python scanner. Fingerprints the platform/version and, with `--probe`, checks whether the server actually decodes Brotli request bodies. |
| **`lab/`** | A Docker lab: a vulnerable target, a patched control, and a *stealth* vulnerable target with its version hidden — so you can see every verdict tier for real. |
> RedSide also runs a **hosted web checker** (enter a URL, get a verdict) built on this same engine.
> That's a separate service; this repo is the open-source CLI tool and lab.
Most public material for this CVE is one of three things, and each is misleading on its own:
- **Weaponized, target-pinned RCE PoCs** that only land against one exact Docker image digest with a
frozen 23 MB payload — impressive, but they prove nothing *your* server and are unsafe to run.
- **Crash / DoS PoCs** that just tip a process over.
- **Version-string checkers** that miss every server which hides its version.
**node2shell does none of those.** It is **detection only** — it never sends the malformed stream
that triggers the bug. Instead it answers the two questions that actually matter for triage:
1. **Is an affected runtime present, and is its version behind the fix?** (headers + health endpoints)
2. **Is the vulnerable code path even reachable** — does the server decompress Brotli bodies at all?
This is a **behavioral** check, so it flags servers that hide their version entirely — the realistic
And the lab proves it honestly: a hidden-version vulnerable target that **only** the behavioral probe
### Run the lab and see every verdict tier
# VULNERABLE (version visible) : node 24.22.0
# PATCHED (control) : node 24.22.1
# STEALTH (version hidden) : node 24.22.0, behavioral-only
python3 scanner/n2s_scan.py -l A WAF/CDN that strips `Content-Encoding: br` can mask the probe, so a clean result does not prove
> the runtime behind it is patched. Always confirm against the real runtime version.
| Node.js | 22.23.4, 24.22.1, 26.12.1 — older lines are EOL (no fix) |
| Envoy / Istio | 1.37.8, 1.38.6, 1.39.3, 1.40.0 |
| Python `Brotli` / `brotlicffi` | 1.2.1 / 1.2.1.0 |
| .NET | Microsoft out-of-band update (Oct 2026) |
| Brotli library (`libbrotlidec`) | 1.0.10, 1.1.1, 1.2.1 |
> Version-based verdicts (`VULNERABLE` / `PATCHED`) are computed for Node.js, Bun and Envoy. .NET and
> Python don't expose a reliable Brotli version over HTTP, so they're flagged by **reachability** (the
> behavioral probe) and top out at `POTENTIALLY VULNERABLE` — confirm those against the runtime.
This is for **lawful, authorized** security testing only — your own systems or targets you have
explicit permission to test. The scanner executes no code on targets and never sends the malformed
stream. The fix is simple: **update the runtime and rebuild your images.**
Under active development · feedback: [email protected] · RedSide Security
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
