Two days after Node.js fixed CVE-2026-111497 and Imperva demonstrated RCE on a stock Node.js image, the story got bigger. The root bug isn't in Node.js — it's in Google's Brotli decompression library, versions 1.0.3 through 1.2.0. Every piece of software that bundles its own copy of that decoder — and there are many — inherited the bug independently. Aikido dubbed the ecosystem-wide exposure HTTP2Shell: an unauthenticated memory-corruption-to-RCE primitive reachable wherever attacker-controlled Brotli data gets decompressed, which in 2026 means a very large slice of the internet's HTTP plumbing. Microsoft shipped out-of-band .NET fixes on a Sunday. Envoy declared it critical. Wiz estimates 91% of cloud environments rely on components carrying the vulnerable code.
To be clear about provenance: IKZERO did not discover this vulnerability. This is our consolidation of the Envoy, Istio, Microsoft, Python and upstream Brotli advisories — the fix matrix in one place, because the hard part of this bug is not understanding it, it's enumerating every copy of it you run.
One decoder, many homes
Brotli is the compression format behind Content-Encoding: br — most modern HTTP clients and servers negotiate it silently. Google's decoder is small, fast, written in C, and vendored everywhere: bundled into runtimes, shipped as a PyPI package, compiled into proxies, linked into media tools. The CVE-2026-111497 out-of-bounds write in c/dec/decode.c means every one of those copies needs its own fix from its own vendor:
| Carrier | Vulnerable | Fixed |
|---|---|---|
| Google Brotli library | 1.0.3 – 1.2.0 | 1.0.10 / 1.1.1 / 1.2.1 |
| Node.js | 22/24/26 pre-release | 22.23.4 / 24.22.1 / 26.12.1 |
| Bun | < 1.4.4 | 1.4.4 |
PyPI Brotli | ≤ 1.2.0 | 1.2.1 |
PyPI brotlicffi | ≤ 1.2.0.2 | 1.2.1.0 |
| Envoy (Brotli decompressor configured) | v1.36.11 – v1.39.2 | 1.40.0 / 1.39.3 / 1.38.6 / 1.37.8 |
| Istio | < 1.29.9 / 1.30.6 / 1.31.2 | 1.29.9 / 1.30.6 / 1.31.2 |
| .NET 8 / 9 / 10 (+ 11 RC) | pre-Oct 25 updates | 8.0.33 / 9.0.22 / 10.0.14 / 11 RC3 |
| Visual Studio 2019/2022/2026 | pre-Oct 25 | 16.11.61 / 17.14.43 / 18.11.2 |
| PowerShell 7.4 / 7.5 / 7.6 | pre-Oct 25 | 7.4.22 / 7.5.13 / 7.6.9 |
| PHP for Windows | builds before the -1 suffix | 8.5.12-1 / 8.4.27-1 / 8.3.36-1 / 8.2.35-1 |
Envoy's exposure deserves a call-out: the advisory applies to deployments with a Brotli decompressor configured — a common but not universal setting for service meshes terminating compressed traffic. Check your config, don't just check your version. And note the PHP entry: Linux PHP users were never exposed through this path; it's specific to the Windows builds, reachable through cURL when automatic response decompression is on (off by default).
The full Node.js picture — including the ten-CVE release it shipped in — is in our Node2Shell companion post.
The Python angle: your HTTP stack has a decoder you didn't configure
The most quietly dangerous carrier is Python. The PyPI Brotli and brotlicffi packages bundle the same vulnerable C decoder, and once installed, popular HTTP clients decompress Brotli responses automatically: requests and urllib3[brotli], httpx, aiohttp[speedups]. Any service that fetches URLs — webhooks, feed readers, crawlers, SSRF-prone fetches, URL preview cards — turns a hostile remote response into input for a memory-corrupting C decoder. The attack doesn't need to reach your users; it needs to reach your outbound fetch.
Two details made this worse in practice:
- PyPI fixes shipped with no advisory entry, so vulnerability scanners that key on advisory databases didn't flag the vulnerable versions. Fix with
pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0"— then verify what your lockfile actually resolved. - Updating the system Brotli library does not fix applications that bundle their own copy. A
apt upgrade libbrotli1leaves every vendored static build untouched.
Beyond HTTP: the FFmpeg path
JFrog researchers extended the attack surface past web servers entirely: FFmpeg, through libjxl, decompresses Brotli metadata inside JPEG XL images — so a crafted image file can reach the vulnerable decoder through your media pipeline. That's a different threat model from HTTP: think upload scanning, thumbnailing services, media-processing queues. Apache Arrow was shown to crash (denial of service) on crafted input as well, and a public tracker still listed DuckDB, Apache Arrow, 7-Zip-zstd, p7zip and EDK II as unpatched days after disclosure.
There's also an uncomfortable root-cause detail: Google fixed the underlying bug upstream in February 2026 — as a routine release, not a security one. Nobody downstream understood what they were pulling in until October. Quiet non-security fixes in foundational libraries are a permanent blind spot in "just keep dependencies updated" advice.
What to do
1. Enumerate every Brotli, not one Brotli. This bug's remediation is an inventory problem:
# Python environments
pip list | grep -i brotli
pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0"
# System copies (note: does NOT fix vendored/static builds)
dpkg -l | grep brotli ; rpm -qa | grep -i brotli
# Vendored/static copies inside your repos and images
grep -rn --include="*.h" -l "br pages" /path/to/vendor 2>/dev/null
docker run --rm <image> sh -c 'find / -name "*brotli*" 2>/dev/null | head'
2. Patch by carrier, using the table above: runtimes first (Node, Bun, Python packages, .NET), then infrastructure (Envoy/Istio meshes), then tooling (PowerShell, VS, media tools).
3. Rebuild and restart. Vendored static links and baked container layers do not pick up system updates. Rebuild images on patched bases, redeploy, and verify inside the running artifact.
4. If you can't patch an Envoy/Istio edge yet, disable Brotli decompression there — Apache mod_brotli and nginx serve Brotli but do not decode untrusted client data by default, so your reverse proxy is likely safe; the mesh layer that decompresses is the risk.
5. Hunt. The signatures mirror the Node post: unsolicited Content-Encoding: br request bodies, node2shell-check-style probing, and unexplained crashes of decompressing services — failed exploit attempts still corrupt memory, and denial of service is the free fallback for every attacker who loses the race.
In the wild: CISA added CVE-2026-111497 to KEV on 27 October, honeypots logged attempts, and Broadcom reports VMware SDDC Manager (Cloud Foundation / vSphere Foundation) affected with fixes still pending — an example of exactly this post's point: the vulnerable copy is somewhere you didn't compile.
The pattern worth noticing
Step back from the fix matrix and the lesson is structural. One memory bug in one small C library, and within a week: a Node.js RCE demo, an ecosystem-wide rename, out-of-band Microsoft patches, a mesh-proxy critical advisory, a media-framework attack path, and KEV listing — with exploitation attempts observed from infrastructure also serving PHP2Shell exploits. Modern software doesn't have dependencies; it has shared organs. The operational defenses are equally structural: an SBOM that can answer "which of our artifacts contain Brotli 1.2.0" in minutes, build processes that rebuild vendored copies rather than carrying them forward, and runtime detection that catches the crash noise even when the RCE fails. That is the boring infrastructure of DevSecOps, and this week is what it's for.
If your answer to "where do we run Brotli?" is currently a shrug, that's the finding. A penetration test will find the reachable copy; our managed SOC will catch the probing; and if you want the SBOM-and-rebuild pipeline built rather than advised about, talk to us.
Frequently Asked Questions
Is HTTP2Shell a different vulnerability from Node2Shell?
No. They are the same CVE — CVE-2026-111497, the out-of-bounds write in Google's Brotli decoder. Node2Shell (Imperva, 22 October) named the Node.js demonstration; HTTP2Shell (Aikido, 23 October) named the ecosystem-wide blast radius: the identical bug inside Python packages, Envoy, Istio, .NET, PowerShell and more. One CVE, many carriers, many fixed versions.
Do I need to patch if I already updated Node.js?
Yes, separately. Node.js shipped a patched copy of Brotli inside the Node binary. Every other carrier — Python Brotli packages, Envoy, .NET, system libraries used by tools like FFmpeg — carries its own copy that the Node update doesn't touch. A Python service next to a patched Node process is still exposed until its own packages are updated.
Why didn't my vulnerability scanner catch the Python versions?
The PyPI Brotli 1.2.1 fix shipped without an advisory database entry, so scanners keying on advisories saw nothing to match. Until tooling catches up, check lockfiles directly: anything resolving Brotli<=1.2.0 or brotlicffi<=1.2.0.2 needs the upgrade, regardless of what your scanner reports.
Is nginx/Apache affected?
Not by default. Apache mod_brotli and nginx compress responses but do not decode untrusted client-supplied Brotli, so the vulnerable path isn't reached in default configs. The exposed infrastructure is the layer that decompresses attacker-controlled input — Envoy and Istio deployments with a Brotli decompressor configured, application runtimes, and media-processing tools.
What's the single most important takeaway?
Treat it as an inventory problem, not a patch. One CVE, dozens of independent copies with different vendors and version numbers — the fix only counts when the copy that's actually reachable in your estate is replaced, rebuilt and restarted. Update the system library alone and you have likely fixed the copy that wasn't reachable.
Sources: NVD/Google CNA; Envoy GHSA-3wvj-j6qm-62mr; MSRC; Aikido; OpenSourceMalware; The Register/JFrog; SecurityWeek; CISA KEV (27 Oct 2026); AWS bulletin 2026-155. Exploit details above are defensive-level and already public.



