Brotli is a way of making data smaller before it travels over the internet, and a large share of web traffic uses it. The code that unpacks Brotli data had a mistake that let specially crafted data write into memory it should not touch. Node.js was the first place anyone reported it, which is why it was first called Node2Shell. Then people checked other software that carries the same unpacking code, and found it in many places. This page lists those places and what to update. The complete PHP2Shell and Node2Shell guide covers the wider picture.
What is the vulnerability, exactly?
It is CVE-2026-111497, also called Node2Shell, HTTP2Shell and Broccoli2Shell. The flaw sits in Google's Brotli C decoder. Vendors name three possible results: a crash, a memory leak and, in some cases, remote code execution. The CVE record, assigned by Google, scores it 9.2 on CVSS 4.0.1
The version numbers need careful reading, because the affected span and the fixed releases overlap numerically. The advisory says Brotli 1.0.3 through 1.2.0 is affected and that users should move to 1.0.10, 1.1.1 or 1.2.1.13 Each fix belongs to its own release branch. Read it by branch:
| Release branch | Affected | Fixed in |
|---|---|---|
| 1.0.x | 1.0.3 through 1.0.9 | 1.0.10 |
| 1.1.x | 1.1.0 | 1.1.1 |
| 1.2.x | 1.2.0 | 1.2.1 |
So 1.1.1 is a fixed release, not an affected one, even though it sits numerically inside "1.0.3 through 1.2.0". Debian's tracker records the introducing change as version 1.0.3 and a separate fixing commit for each of the three branches.8 The advisory states one range and three fixed releases. The branch-by-branch listing above is my reading of that text, and it matches the per-branch lists in Tanium's and Wiz's write-ups.17 Upstream released the fixed versions on 21 October 2026. Tanium says the upstream fix was committed on 27 February with no advisory.1
Reported CISA added it to the Known Exploited Vulnerabilities catalog on 27 October and gave no attack detail.2 Reports put the federal deadline at 10 November.34 Status as of 4 November 2026.
Despite the "HTTP2Shell" name, this has nothing to do with the HTTP/2 protocol.1 The name changed because the bug lives in the library, not in one runtime.
Where else is it, and what do I update to?
Each copy of the decoder is fixed by whoever ships it. Read the "Note" column, because several rows depend on how the product is configured or built.
| Ecosystem | What carries the copy | Fixed version | Note |
|---|---|---|---|
| Google Brotli | The upstream C library | 1.0.10 (1.0.x branch), 1.1.1 (1.1.x), 1.2.1 (1.2.x) | Affected: 1.0.3 to 1.0.9, 1.1.0 and 1.2.0.138 |
| Node.js | The runtime's bundled decoder | 22.23.4, 24.22.1, 26.12.1 | Node's advisory names node:zlib as the route.5 See [[patch-node2shell-nodejs-22-24-26 |
| Python | PyPI packages Brotli and brotlicffi | Brotli 1.2.1; brotlicffi 1.2.1.0 | Affected from 1.0.3 up to those versions. Each virtualenv, CI runner and container has its own copy.131 |
| .NET | Brotli support in System.IO.Compression | 8.0.33, 9.0.22, 10.0.14, 11 RC3 | From Microsoft's advisory. .NET Framework is not affected.6 |
| Bun | The runtime's bundled decoder | 1.4.4 | 1.0.21 through 1.4.3 are affected. Executables built with Bun's compile option keep the old decoder until rebuilt.71 |
| Envoy | Its Brotli decompressor | 1.40.0, 1.39.3, 1.38.6, 1.37.8 (also 1.36.12 on the unsupported 1.36 branch) | Affects deployments with the decompressor configured.114 |
| Istio | Envoy data plane | 1.31.2, 1.30.6, 1.29.9 | 1.29 reached end of life on 15 October.714 |
| Go | andybalholm/brotli | v1.2.7 | A separate implementation. Tanium says the likely effect is a crash.17 |
| Debian | The brotli source package | 12 (bookworm): 1.0.9-2+deb12u1. 13 (trixie): 1.1.0-2+deb13u1. Unstable (sid): 1.2.0-4.1 | Testing (forky) 1.2.0-4 was still listed vulnerable on 4 November. Debian's nodejs and python-brotlicffi use the system library.8 |
| Ubuntu | The brotli, libbrotli1 and python3-brotli packages | 22.04: 1.0.9-2ubuntu0.1. 24.04: 1.1.0-2ubuntu0.1. 26.04: 1.2.0-3ubuntu0.1. 26.10: 1.2.0-4ubuntu0.1 | From USN-8956-1. 16.04 to 20.04 get fixes through Ubuntu Pro (USN-8956-2).910 |
| PHP for Windows | Bundled Brotli in the Windows builds | Rebuilt packages 8.5.12-1, 8.4.27-1, 8.3.36-1 and 8.2.35-1 | Reported by iTnews and The Hacker News.1511 |
How I checked these numbers: I read Microsoft's .NET post, Debian's tracker and a complete copy of Ubuntu's USN-8956-1 directly. Python's ranges come from the Brotli advisory record. The Bun, Envoy, Istio and Go figures come from those projects' advisories (Bun GHSA-663w-cv8g-jj2p, Envoy GHSA-3wvj-j6qm-62mr, Istio ISTIO-SECURITY-2026-007) as relayed by Tanium, Wiz and Field Effect, which agree with each other. I could not open the advisories themselves. Confirm those four rows against the projects' pages before you act on them.
Tanium reported on 24 October that Red Hat, SUSE, Amazon Linux 2023 and Fedora had no fix yet.1 I did not recheck those, so read your vendor's tracker before assuming anything. Distribution fixes move quickly.
Common misreading: "I updated my system's Brotli package, so I'm fine." Only for software that uses the system library. Node.js bundles its own decoder, so a system update does not touch it.5 Debian's tracker says its own nodejs package uses the system library, which is why the answer depends on how each program is built.8 Microsoft says the same about self-contained .NET apps: a newer runtime on the host does not update the one bundled in the app.6
Common misreading: "I'll update the brotli npm package." The Node.js flaw lives in the runtime binary, not in an npm package. Node's advisory places it in the decoder bundled with Node.js, so the fix is a Node.js release.5
Common misreading: "The version string says 1.1.0, so I'm unpatched." Per Tanium, a fixed Node.js can still report an old Brotli version, so check the Node.js version itself.1 Distributions also backport fixes without changing the headline number. Ubuntu's 24.04 fix is still 1.1.0, as package release 1.1.0-2ubuntu0.1.9 Compare the full package release against the vendor advisory.
What does finding your copy look like?
Take a Python service that calls a partner API with requests. The response comes back Brotli-compressed, and the library unpacks it with its own Brotli copy. Per Tanium, urllib3, requests, httpx and aiohttp decode Brotli automatically whenever Brotli or brotlicffi is installed, and your code never has to import either.1
So the first question is which copy your service has. In the environment that runs the service, ask pip:
python -m pip show Brotli brotlicffi
For a container, run the same command against the live container with docker exec <container> python -m pip show Brotli brotlicffi. The output gives each installed package's Version and Location. If the version is below 1.2.1 for Brotli, or below 1.2.1.0 for brotlicffi, that copy is yours to fix: upgrade it, regenerate your lockfile, rebuild the image and restart the service. If the package came from your Linux distribution (python3-brotli), the fix arrives through your distribution's update instead.
The same output has a Required-by line, but treat it carefully. It can be empty, and it does not reliably show why a package was installed. To find which dependency pulled Brotli in, use a dependency-tree tool such as pipdeptree in reverse mode for that package, or read your lockfile.
Nothing in this example touches the bug itself. The point is only that someone has to find the copy and own the fix.
How do I find every copy?
Brotli rarely shows up as an application, because it is compiled into other things. Tanium's list of where copies hide is a good checklist:1
- Node.js runtimes installed outside package managers, such as nvm, fnm, Volta and Homebrew.
- Applications that ship their own Node.js, including desktop apps and developer tools.
- Python environments, which often get Brotli as a transitive dependency of an HTTP client.
- Bun binaries, including standalone executables.
- System libbrotli packages.
- Container images, which keep the runtime from the day they were built.
- Go and Java libraries, proxies such as Envoy and Istio, and Electron apps.
For software that vendors Brotli's source, one clue is the new error name BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH, which the upstream fix adds.1213 Finding it in a copy suggests the fix is present. Not finding it is only a lead that the copy may be unfixed. Vendors can backport a fix under different code, modify the implementation or use another code path, so the name is not a vulnerability test. The vendor advisory, the patch diff or a verified fixed build is what settles it.
A software bill of materials (SBOM) helps here, and Tanium says scanners were slow to map this CVE to the Python packages.1 So don't read a quiet scanner as proof that you have no copies.
Who is exposed in practice?
Exposure needs two things: a vulnerable decoder, and a path for attacker-controlled Brotli data to reach it. Both directions count. A server can decompress a request body, and a client or service can decompress a response from a server it fetches. Tanium names URL previews, webhook targets and crawlers as examples of the second direction.1
Some products only reach the decoder when a feature is turned on. In .NET, ASP.NET Core request decompression and automatic HttpClient response decompression are not on by default.6 Envoy and Istio are exposed only when a Brotli decompressor is configured.1 For Node.js, Node's advisory names data an application decompresses with node:zlib.5 Whether your web server is affected has its own page: whether your web server is affected.
Exposure through a feature being off is not the same as no copy. Patch the copies anyway. Features get turned on later.
What do I do?
- Inventory every copy, using the list above.
- Patch each ecosystem on its own schedule, using the table.
- Rebuild and redeploy container images, and rebuild self-contained apps and Bun executables.
- Restart every process, because a running process keeps the decoder it loaded.
If you can't update right away, Tanium lists interim steps: reject requests with Content-Encoding: br at your WAF or proxy, remove the Brotli decompressor filter in Envoy or Istio, and uninstall Brotli and brotlicffi in Python, which makes HTTP clients fall back to gzip.1 These are bridges, not equivalents of patching. A WAF covers inbound requests only, not Brotli responses your own code fetches. For Express, see how an Express app decodes Brotli.



Comments