Updated October 27, 2026: .NET is now fixed (out-of-band, October 25), along with the Ruby gem, PHP extension, MuPDF and UEFITool. Added a live exposure tracker, an affected-and-fixed summary, a “How to disable Brotli decoding while you patch” section, Microsoft’s and Red Hat’s severity ratings, the public PoC’s limits, Ubuntu 24.04’s fix, the Bun affected range and newly fixed bindings.
Contents
ToggleExecutive Summary
HTTP2Shell, first circulated as Node2Shell, is an out-of-bounds write in Google’s reference C Brotli decoder (c/dec/decode.c), tracked as CVE-2026-111497 and GHSA-9v8h-qqgm-4m35. Brotli 1.0.3 through 1.2.0 is affected. A crafted Brotli stream writes past an internal buffer and corrupts adjacent decoder state, which can leak memory, crash the process, or, under the right heap conditions, hand the attacker code execution. The nickname is unrelated to the HTTP/2 protocol; it reflects that HTTP content decoding is the most common path to the decoder.
The bug became a cross-ecosystem problem within three days. Node.js and Bun bundle the library. So do Envoy, the .NET runtime, Chromium, cdxgen’s standalone binaries and a long list of data and document tools. On the Python side, the PyPI Brotli and brotlicffi packages ship the vulnerable decoder, and requests, urllib3, aiohttp and httpx will use either one automatically to decode Content-Encoding: br responses. An application can be exposed without a single import of Brotli in its own code.
Patches exist for the main runtimes; the harder work is finding where the decoder is reachable. The attack runs in both directions: a hostile client can hit a server that inflates request bodies, and a hostile server can hit any client that decodes compressed responses. Webhooks, link previews, crawlers, URL importers and browsing AI agents are in scope, and inbound WAF rules do not cover any of them. Most vulnerable copies are vendored inside runtimes, wheels and static binaries, so manifest-based SCA reports them as clean.
Key takeaways
- Patch the runtime, not the lockfile. npm update changes nothing; Node.js, Bun and system libbrotli carry the decoder.
- The outbound path matters as much as the inbound one. Webhooks, link previews, crawlers and browsing AI agents decode attacker-served responses that a WAF never inspects.
- Python exposure was invisible to CVE-keyed tooling at disclosure. The PyPI fixes shipped without advisory text and the GHSA still shows no CVE ID in its CVE field.
- Treat every PyPI Brotli release below 1.2.1 as vulnerable. The same unchecked pattern exists in releases older than the GHSA’s 1.0.3 floor.
- One CVE arrives as four kinds of finding (runtime, SCA, distro package, vendored copy). Run it as one campaign or it will be closed four times and fixed once.

TL;DR for Engineering Teams
| What it is | Out-of-bounds write in the Brotli C decoder, CVE-2026-111497, CVSS 4.0 9.2 Critical from the CNA (CWE-787 caused by CWE-252, an unchecked return value) |
| Where it bites | Brotli 1.0.3 to 1.2.0 wherever it is linked or vendored: Node.js 22/24/26, Bun 1.4.3 and earlier, PyPI Brotli and brotlicffi, Envoy and Istio with the Brotli decompressor, .NET BrotliStream and HttpClient auto-decompression, cdxgen standalone binaries and images |
| Why it matters | Unauthenticated and reachable both inbound (request bodies) and outbound (fetched responses). Imperva showed code execution on a stock Node.js 24.22.0 arm64 build. CISA SSVC on 23 October: exploitation none, automatable no, technical impact total. A public PoC reaches code execution only on a deliberately weakened build; Red Hat rates the flaw Important. Not in KEV |
| Patch status | Partial, and moving fast. Of roughly 80 tracked products, about 38 are fixed, 12 have a fix in progress and 19 are still vulnerable as of October 26. Still open: Python wheels that vendor their own decoder (Pillow, pyarrow, curl-cffi, pycurl), DuckDB, ClickHouse, Apache Arrow bundled builds, v8-jsi, 7-Zip-zstd, p7zip, EDK II, woff2 Makefile builds and Git for Windows. |
| Immediate action | Upgrade Node.js and Bun, rebuild images, pip install -U “Brotli>=1.2.1” “brotlicffi>=1.2.1.0”, restart processes. Until then, reject br request bodies at the edge and turn off automatic response decoding where services fetch attacker-chosen URLs |
Summary: affected and fixed versions at a glance
One table for fast triage. Upgrade the component on the left to the fixed version on the right; full per-ecosystem detail follows below.
| Component | Affected | Fixed in | Action |
|---|---|---|---|
| Google Brotli (C library) | 1.0.3 – 1.2.0 | 1.0.10, 1.1.1, 1.2.1 | Patch OS package or vendored copy |
| Node.js 22 / 24 / 26 | < 22.23.4 / < 24.22.1 / < 26.12.1 | 22.23.4, 24.22.1, 26.12.1 | Upgrade runtime, rebuild, restart |
| Node.js 20 / 23 / 25 | all | none (EOL) | Migrate to 22, 24 or 26 |
| Bun | 1.0.21 – 1.4.3 | 1.4.4 | Upgrade; rebuild compiled binaries |
| PyPI Brotli / brotlicffi | < 1.2.1 / < 1.2.1.0 | 1.2.1 / 1.2.1.0 | Upgrade; audit vendored wheels |
| .NET 8 / 9 / 10 | < 8.0.33 / < 9.0.22 / < 10.0.14 | 8.0.33, 9.0.22, 10.0.14 | Upgrade runtime (Framework not affected) |
| Envoy | 1.39.2, 1.38.5, 1.37.7, 1.36.12 | 1.40.0, 1.39.3, 1.38.6, 1.37.8 | Upgrade (if Brotli decompressor on) |
| Istio | 1.30.0-1.30.5, 1.31.0-1.31.1 | 1.30.6, 1.31.2 | Upgrade |
| brotli4j (Java) | < 1.24.0 | 1.24.0 | Upgrade |
| Ruby brotli / PHP ext | < 0.9.1 / < 0.21.1 | 0.9.1 / 0.21.1 | Upgrade |
| Perl IO::Compress::Brotli | < 0.023 | 0.023 | Upgrade (CVE-2026-115936) |
| Go andybalholm/brotli | < v1.2.7 | v1.2.7 | Upgrade (crash-only) |
| Debian 12 / 13 brotli | 1.0.9-2 / 1.1.0-2 | 1.0.9-2+deb12u1 / 1.1.0-2+deb13u1 | apt upgrade (DSA-6582-1), restart |
| Still unpatched (Oct 26) | Pillow, pyarrow, DuckDB, ClickHouse, Arrow, v8-jsi, 7-Zip-zstd, p7zip, EDK II, woff2, Git for Windows | none yet | Inventory, mitigate, watch vendor |

Note: Node.js reports process.versions.brotli as 1.2.0 even after patching, because the fix was cherry-picked. Check the Node.js version, not that string.
Vulnerability Overview
| Field | Value |
|---|---|
| Names | HTTP2Shell, Node2Shell, Python2Shell, Broccoli2Shell (one bug) |
| Vendor | Google (CNA and upstream); redistributed by Node.js, Bun, PyPI maintainers, Envoy, Microsoft and others |
| Product | Brotli reference C decoder, c/dec/decode.c |
| Vulnerability type | Out-of-bounds write, heap memory corruption (CAPEC-100) |
| CWE | CWE-787 Out-of-bounds Write; CWE-252 Unchecked Return Value |
| CVSS | 4.0 9.2 Critical (CNA): CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. Node.js: High. Red Hat: Important. Amazon Linux: 8.1 (CVSS 3.1). Microsoft: Critical, 8.1 for .NET (AC:H). Microsoft: 8.1 for the .NET path (AC:H, higher attack complexity). Istio: 7.7 for its own exposure. NVD: awaiting analysis |
| Identifiers | CVE-2026-111497, GHSA-9v8h-qqgm-4m35, PYSEC-2026-4322 (Brotli), PYSEC-2026-4323 (brotlicffi), Envoy GHSA-3wvj-j6qm-62mr, Bun GHSA-663w-cv8g-jj2p |
| Reported by | Khai Tran and Sylvie Mayer, OpenAI Codex Security. Node.js fix by Matteo Collina and Antoine du Hamel |
| Patch available | Yes upstream and in major runtimes; no for many vendored copies |
| Active exploitation | Not confirmed. CISA SSVC: exploitation none (23 October). The public PoC reaches code execution only on a deliberately weakened build; on standard builds it crashes. Red Hat: no reliable public PoC for code execution |
| CISA KEV | Not listed as of 24 October 2026 |
Technical Anatomy
Root cause
The flaw sits in block-switch handling. DecodeBlockTypeAndLength() returned a boolean, so callers could not tell “this input is malformed” from “I need more bytes.” The decoder’s fast path also dropped that return value, which is why the CVE record carries CWE-252 alongside CWE-787.
A crafted stream can drive the decoder into a block switch that valid input never produces. The decoder reads the failure as a request for more input and keeps appending attacker bytes to an internal buffer sized for eight bytes. Bytes past that boundary land on adjacent decoder state.
Upstream commit 0ed4268 (“Detect and report invalid stream earlier”) makes the function return a real error code on both the safe and fast paths: BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (-17). Node.js cherry-picked that commit and exposes the new constant. Memory-safe ports hit the same logic error but fail safely: the Go port andybalholm/brotli can only panic, and the Rust brotli-decompressor returns an error.
Exploit path
- Entry point. The attacker puts a Brotli stream in front of a decoder. Inbound, that is a request body with Content-Encoding: br sent to a parser that inflates it (Express 5 and body-parser 2.x do this by default; a plain Node.js http server does not decode bodies on its own). Outbound, it is a response from an attacker-controlled host read by Node or Bun fetch(), urllib3, requests, aiohttp, httpx, Scrapy or libcurl with automatic decoding. File inputs also work: Parquet, JPEG XL, WOFF2, PDF, archives and firmware images.
- Vulnerability trigger. The stream encodes an invalid block switch. The fast path ignores the failure, the decoder waits for “more input,” and attacker bytes overrun the eight-byte buffer into neighboring decoder state.
- Execution impact. Memory disclosure or a crash at minimum. Imperva’s red team ran attacker-supplied code on an unmodified Node.js 24.22.0 Linux arm64 build, delivered both as a fetch() response and as an Express request body. It took several attempts, has not been released, and was not tested on other builds. For Python, corruption is confirmed under AddressSanitizer; no exploitation attempt has been published.
This article stays at the root-cause level on purpose. Exploit primitives beyond that are left out.

Disclosure timeline
The fix sat in public source for eight months before anyone labeled it a security issue. The CVE record dates public disclosure to 27 February 2026 for that reason, and every project carrying its own copy is now shipping late.
| Date (2026) | Event |
|---|---|
| 27 Feb | Upstream commit 0ed4268 lands without a security label |
| 21 Oct | Node.js ships 22.23.4, 24.22.1, 26.12.1 (High). Google tags Brotli 1.0.10, 1.1.1, 1.2.1. Vercel deploys inbound WAF rules. “Node2Shell” appears |
| 22 Oct | CVE published by Google; GHSA-9v8h-qqgm-4m35 published; PyPI Brotli 1.2.1 released; Bun 1.4.4 ships; Imperva publishes RCE research. Amazon Linux 2023 lists brotli and nodejs24 as pending |
| 23 Oct | brotlicffi 1.2.1.0 on PyPI; CISA SSVC assessment; PYSEC-2026-4322 and -4323 added; GHSA review maps both PyPI packages; Envoy GHSA-3wvj-j6qm-62mr; Debian trixie fixed in 1.1.0-2+deb13u1; Go port fixed; the name “HTTP2Shell” appears |
| 24 Oct | Bun adopts the HTTP2Shell name and publishes GHSA-663w-cv8g-jj2p. Ruby gem 0.9.1, PHP extension 0.21.1 and UEFITool A76 ship fixes |
| 25 Oct | Cloudflare emergency WAF release; Azure WAF guidance |
Two corrections to circulating coverage. OpenAI did not find the bug in February; the root cause was fixed then without being recognized, and OpenAI found the vulnerability during a recent red-teaming exercise. Python also issued no advisory of its own; the Python impact comes from OpenSourceMalware and the PyPA PYSEC records.
Affected Versions
Fixed
| Package | Vulnerable Versions | Fixed Version | Notes |
|---|---|---|---|
| Google Brotli (C) | 1.0.3 to 1.0.9, 1.1.0, 1.2.0 | 1.0.10, 1.1.1, 1.2.1 | CVE record ranges; source of every other copy |
| Node.js 22.x | < 22.23.4 | 22.23.4 | Bundled copy; OS patch does not fix official binaries or Docker images. Official node Docker images were rebuilt on October 21: repin tags to 22.23.4 / 24.22.1 / 26.12.1 and rebuild with –pull |
| Node.js 24.x | < 24.22.1 | 24.22.1 | Imperva RCE shown on 24.22.0 arm64 |
| Node.js 26.x | < 26.12.1 | 26.12.1 | |
| Node.js 18.x, 20.x, 23.x, 25.x | All | None | End of life |
| Bun | 1.0.21 to 1.4.3 | 1.4.4 | Rebuild bun build –compile executables |
| .NET 8 / 9 / 10 | < 8.0.33 / < 9.0.22 / < 10.0.14 | 8.0.33, 9.0.22, 10.0.14; .NET 11 RC3 | Out-of-band, October 25. Brotli decompression is off by default, so only apps that opted in are exposed. Also patched: Visual Studio 2019 / 2022 / 2026, PowerShell 7.4 / 7.5 / 7.6 and Azure Linux. .NET Framework not affected |
| PyPI Brotli | 1.0.4, 1.0.6, 1.0.7, 1.0.8, 1.0.9, 1.1.0, 1.2.0 | 1.2.1 | PYSEC-2026-4322; released versions in range per PyPI |
| PyPI brotlicffi | 1.0.9.0 to 1.0.9.2, 1.1.0.0, 1.2.0.0 to 1.2.0.2 | 1.2.1.0 | PYSEC-2026-4323 |
| Envoy | 1.39.2, 1.38.5, 1.37.7, 1.36.11 and earlier | 1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12 | Only with the Brotli decompressor configured |
| Istio | 1.31.0 to 1.31.1, 1.30.0 to 1.30.5, 1.29.0 to 1.29.8 | 1.31.2, 1.30.6, 1.29.9 | Only with Brotli decompression enabled; 1.29 is end of life |
| cdxgen standalone binaries and images | 13.4.0 and earlier, 12.8.8 and earlier | 13.5.0, 12.8.9 | npm install uses system Node instead |
| Debian brotli | bookworm, trixie | 1.0.9-2+deb12u1, 1.1.0-2+deb13u1 | DSA-6582-1. Backports keep the old upstream version string; sid unfixed |
| Ubuntu brotli | 24.04 | libbrotli1 1.1.0-2ubuntu0.1 | Other releases per the Ubuntu tracker |
| Alpine 3.24 brotli | < 1.2.1-r0 | 1.2.1-r0 | |
| Go andybalholm/brotli | Before v1.2.7 | v1.2.7 | Crash only |
| brotli4j (Java) | < 1.24.0 | 1.24.0 | |
| Perl IO::Compress::Brotli | < 0.023 | 0.023 | CVE-2026-115936 |
| Ruby brotli gem, PHP brotli extension, UEFITool, MuPDF | Bundled copies | 0.9.1, 0.21.1, A76, 1.28.0 | Fixed 24 October; MuPDF 1.27.x still affected |
| Chromium, Firefox, ngx_brotli, Ghostscript, SumatraPDF | Vendor-specific | Reported fixed | Electron apps carry Chromium and Node copies |
| curl for Windows, VS Code Server | Bundled copies | Vendor updates | Reported fixed |
Not fixed yet
| Package | Vulnerable Versions | Fixed Version | Notes |
|---|---|---|---|
| DuckDB, ClickHouse | Current | Pending (PR #27791, PR #127419) | Parquet input |
| Apache Arrow bundled build | Bundles 1.0.9 | None | Parquet input |
| libjxl, NanaZip, 7-Zip-zstd, p7zip | Current | None | File-format input |
| EDK II | Current | None | Firmware images |
| woff2 (Makefile build) | Bundles 1.0.3 | None | WOFF2 fonts; the CMake build links the system library |
| GraalJS / v8-jsi, YDB | Current | None | |
| Pillow, pyarrow, curl-cffi, pycurl | Vendored decoder in wheels | None | Upgrading Brotli alone does not fix them |
| Git for Windows | Bundled copy | None | Watch the vendor |
| Debian nodejs | Current | Pending | Distro packages lag upstream |
Pure Java, JavaScript and C# ports (pdf.js, iText, HtmlUnit) are not affected.
Live exposure tracker
The bug moves product by product, and the status changes daily. Our live heat map tracks which products that bundle the Brotli decoder are vulnerable, in progress or fixed, day by day.
Open the live tracker in a new tab →
Exposure Analysis
| Environment | Risk Level | Reason |
|---|---|---|
| Internet-facing Node.js APIs on Express 5 / body-parser 2.x | Critical | express.json() inflates br bodies by default; unauthenticated reach |
| Services fetching attacker-chosen URLs (webhooks, link previews, URL import, image proxies, OIDC discovery) | Critical | Hostile server returns a br response; inbound WAF rules never see it |
| Browsing AI agents and crawlers | Critical | Every fetched page is attacker input to an auto-decoding client |
| Envoy / Istio with the Brotli decompressor | High | Proxy decodes before application controls apply |
| CI/CD runners, build tools, SBOM generators (cdxgen binaries) | High | Scan and download untrusted input; runners hold tokens and signing keys |
| Data pipelines (DuckDB, Arrow, ClickHouse) on third-party Parquet | High | File-borne trigger, no fix yet |
| .NET services with Brotli decompression enabled | Medium | Fixed October 25; decompression is off by default, so only services that opted in are exposed |
| Developer workstations, Electron apps | Medium | Two embedded copies per Electron app; local secrets |
| Apache / Nginx default configs | Low | Encoder only; request bodies not decoded by default |
The Python exposure has a trap built in. Earlier urllib3 2.6.x decompression fixes told users to move to at least Brotli 1.2.0 or brotlicffi 1.2.0.0, and the urllib3[brotli] extra enforced those floors. Teams that followed that guidance landed on the last vulnerable releases. Any environment with either package installed, directly or through an extra, decodes br responses in requests and aiohttp automatically.
Real-World Impact
No in-the-wild exploitation has been reported. CISA’s SSVC entry on 23 October records exploitation as none and automatable as no, with total technical impact, and Red Hat reports no reliable public PoC for code execution. Published PoC code reportedly reaches code execution only against deliberately weakened builds. That gap will close: the patch diff is public, the trigger condition is small, and downstream release notes already describe reachable paths in plain language. Vendure states its Shop API accepts Brotli request bodies, so an unauthenticated request reaches the vulnerable code. Bulwark webmail describes a signed-in user pointing the server at a calendar or WebDAV host that returns a crafted response.
The realistic scenarios follow the outbound path. An attacker registers a webhook or submits a URL, the backend fetches it, the response decodes, and the attacker lands in a process that usually holds cloud credentials, database secrets and service tokens. CI runners and SBOM generators that scan untrusted artifacts sit in the same position with higher-value secrets. Inbound, any Express 5 API that accepts JSON is reachable with one POST. Memory disclosure is the lower bar; leaked heap from a shared process can expose other users’ request data.
Scale comes from install base, not telemetry. Node.js and requests sit under most server-side web code, and the vendored list covers browsers, the .NET runtime, data warehouses and firmware tooling. Phoenix is not quoting a Shodan figure, because exposure depends on decoder configuration and fetch behavior that banner scans cannot see.
Detection Guidance
Log indicators
| Signal | Where | Meaning |
|---|---|---|
| Content-Encoding containing br on requests (any case, including gzip, br) | Edge proxy, WAF, API gateway logs | Inbound attempts; legitimate clients rarely send Brotli bodies to JSON APIs |
| BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH or error -17 | Application logs on patched Node.js and Brotli 1.2.1 | Malformed block switch rejected; likely a probe |
| SIGSEGV / SIGABRT or heap-corruption aborts in node, bun, python, envoy | Container restarts, journald, crash reporters | Possible trigger on an unpatched host, especially after a br request or outbound fetch |
| Outbound fetches to new or user-supplied hosts answering Content-Encoding: br | Egress proxy logs | Outbound path; correlate with webhook, preview and import features |
| Parquet, JPEG XL, WOFF2 or PDF uploads followed by worker crashes | Ingestion logs | File-borne trigger against unpatched tooling |

Scanner coverage
Do not trust early-week scan results. The CVE record names only github.com/google/brotli, and reporting on 24 October said the GHSA still listed no affected package. The GHSA page has since been reviewed and maps brotli >=1.0.3,<1.2.1 and brotlicffi >=1.0.3,<1.2.1.0, and OSV carries the PYSEC entries. Re-run pip-audit, Dependabot, Safety and Phoenix SCA ingestion, then confirm by hand that findings appear.
Manifest scanning still misses most copies. The decoder inside Node.js, Bun, Envoy, .NET, cdxgen binaries and wheels never appears in a lockfile. Distro backports cut the other way: Debian’s fixed 1.0.9-2+deb12u1 still reads as 1.0.9, so naive version matching yields false positives. Node.js cherry-picked the fix, so judge Node by the Node version, not process.versions.brotli.
Advisory data for HTTP2Shell changed daily this week, and it will keep changing as vendored copies get fixed. Phoenix Blue, Phoenix Security’s free threat intelligence feed, follows issues like this one as they move: from a quiet upstream commit to a published CVE, from one runtime to every product that vendors the same code, and from claimed RCE to demonstrated RCE. Sign up free at phxintel.security.
Protect yourself with the latest threat intelligence, get free access to Phoenix Blue Today
Verification steps for teams
- Inventory runtimes, including base images: docker run –rm <image> node -v and bun –version. Below 22.23.4, 24.22.1, 26.12.1 or 1.4.4 is vulnerable.
- Check Python lockfiles and live environments: grep -RniE ‘^(brotli|brotlicffi)[=<>~ ]’ requirements*.txt poetry.lock uv.lock Pipfile.lock and python -c “import brotli; print(brotli.__version__)”, repeated for brotlicffi. Look for extras such as urllib3[brotli], httpx[brotli] and aiohttp[speedups].
- Find vendored native copies: find / -name ‘libbrotlidec*’ 2>/dev/null, plus strings -a <binary> | grep -m1 BrotliDecoderDecompressStream across build artifacts, Electron bundles, cdxgen binaries and site-packages.
- Search proxy config for envoy.compression.brotli.decompressor, including Istio EnvoyFilter manifests.
- Map every outbound fetch feature to its runtime and HTTP client and mark it reachable or not.
- Regenerate SBOMs from built images rather than source manifests and diff them against the tables above.
Remediation Guidance
Immediate actions
- Upgrade Node.js to 22.23.4, 24.22.1 or 26.12.1 and Bun to 1.4.4. Rebuild and redeploy every image and compiled executable; a running process keeps the old decoder until it restarts.
- Upgrade Python: pip install -U “Brotli>=1.2.1” “brotlicffi>=1.2.1.0”, pin those floors in lockfiles and constraints files, and restart long-running workers.
- Upgrade system libbrotli where Node, libcurl or Python link it dynamically (Debian nodejs, libcurl on Debian 12 and Ubuntu 24.04), then restart the processes that loaded it.
- Upgrade Envoy to 1.36.12, 1.37.8, 1.38.6, 1.39.3 or 1.40.0 and Istio to 1.30.6 or 1.31.2 where the Brotli decompressor is on. Plan the move off Istio 1.29.
- Upgrade cdxgen standalone binaries and images to 12.8.9 or 13.5.0.
- Move workloads off Node.js 18.x, 20.x, 23.x and 25.x.
- Upgrade .NET to 8.0.33, 9.0.22 or 10.0.14, even if you applied the October 13 update, and update Visual Studio and PowerShell. Apps using Blazor WebAssembly also need updated NuGet package references. .NET Framework is not affected.
- Rotate credentials held by any internet-facing process that ran a vulnerable decoder on untrusted br input. Memory disclosure leaves no clean trail.
Temporary mitigations
Inbound, reject requests whose Content-Encoding contains br before any proxy or body parser decodes them, on direct-to-origin routes as well as the CDN. In Express, put a guard ahead of every parser or set inflate: false:
app.use((req, res, next) => {
const ce = (req.headers['content-encoding'] || '').toLowerCase();
if (ce.split(',').some(v => v.trim() === 'br')) return res.status(415).end();
next();
});
Outbound, dropping br from Accept-Encoding does not help, because a server can send Brotli regardless. Disable automatic decoding for untrusted fetches and check Content-Encoding before reading the body; in urllib3, use preload_content=False and read with decode_content=False. Node.js fetch() has no published workaround, so the runtime upgrade is the only fix there.
For Python services where Brotli is optional, uninstall both Brotli and brotlicffi and drop extras such as httpx[brotli]; clients fall back to gzip and deflate. For Next.js, images.unoptimized stops the optimizer fetching and decoding remote images. For Envoy and Istio, remove the Brotli decompressor or disable both its directions, and leave gzip and zstd entries that share the envoy.filters.http.decompressor name. For .NET, upgrade the runtime. Until then, keep Brotli out of HttpClientHandler.AutomaticDecompression and ASP.NET Core request decompression on services that handle untrusted input; both are off by default. Quarantine third-party Parquet with Brotli-compressed columns until DuckDB, Arrow and ClickHouse ship fixes. Cloudflare, Azure, Imperva and Vercel WAF rules cover inbound traffic only. Watch the mode: Cloudflare’s two managed rules ship in Log mode and must be switched to Block; Azure DRS 2.2 rule 920450 blocks in Prevention mode.
How to disable Brotli decoding while you patch
Turning off Brotli decoding cuts the path to the vulnerable decoder. Each tool handles it differently, and blocking Brotli in one place does not protect the others.
curl and libcurl. curl only decodes compressed responses when you pass –compressed. Remove it from scripts, aliases, CI jobs and .curlrc. Setting an Accept-Encoding header with -H does not turn decoding on. In libcurl apps, leave CURLOPT_ACCEPT_ENCODING at its NULL default, which stops decompression; setting it to “gzip, deflate” does not protect you, because a malicious server can answer with Brotli regardless. Check whether your build decodes Brotli with curl -V and look for brotli in the Features line.
Python. Where Brotli is optional, uninstall Brotli and brotlicffi, remove extras such as httpx[brotli] and anything that would reinstall them, regenerate lockfiles, and rebuild and restart workloads. Requests, urllib3 and HTTPX keep gzip and deflate without these packages, though a server that sends Brotli anyway may cause an unreadable response or an error. This does not remove decoders bundled into other runtimes or wheels.
Node.js and Bun. Reject requests whose Content-Encoding includes br in middleware or at the proxy, before Express 5 or body-parser 2.x decodes the body. fetch() decodes Brotli responses automatically with no standard switch to disable, so upgrading the runtime is the only fix for responses your app fetches.
Envoy and Istio. Envoy only decodes Brotli when the decompressor filter is configured, which Istio does not do by default. Find it in your EnvoyFilters:
kubectl get envoyfilters -A -o yaml \
| grep -nE 'compression\.brotli\.decompressor|brotli\.decompressor\.v3\.Brotli'
Check a running sidecar or gateway:
istioctl proxy-config listeners <pod> -n <namespace> -o json \
| grep -nE 'compression\.brotli\.decompressor|brotli\.decompressor\.v3\.Brotli'
If either returns a match, remove the Brotli decompressor from the EnvoyFilter and run the second command again to confirm it is gone from the live config. Using Brotli only to compress responses does not reach the vulnerable code.
Apache. mod_brotli compresses responses and does not decode them, so a standard Apache setup does not run the vulnerable decoder.
Phoenix Security Recommendations
HTTP2Shell is a vendored-library problem first, so the work is separating “the advisory applies to this artifact” from “this decoder is reachable with untrusted input in production.”
Contextual deduplication collapses the same CVE reported by SCA on requirements.txt, by container scanning on the Node base image and by the distro scanner on libbrotli into one finding per asset. Without it, one Node service can surface as three tickets with three owners and three version strings, one of them a Debian backport false positive.
Binary and SBOM evidence closes the manifest gap. Phoenix ingests image-level SBOMs and runtime package evidence, so a Node 24.22.0 binary, a Bun-compiled executable, a cdxgen binary or a .NET service carrying Brotli 1.1.0 shows up even when no lockfile names Brotli. Mark those as vendored so owners know a dependency bump will not fix them.
Reachability analysis ranks what is left. An internet-facing Express 5 service with express.json(), a webhook worker fetching customer URLs and an Envoy mesh with the Brotli decompressor go first. A batch job with Brotli installed and no untrusted fetches waits. Attack surface management supplies the internet-facing half and ties it to the owning team.
Run it as a remediation campaign:
- Create the HTTP2Shell campaign scoped to CVE-2026-111497, GHSA-9v8h-qqgm-4m35, PYSEC-2026-4322 and PYSEC-2026-4323, with fixed versions as targets.
- Assign owners automatically through vulnerability ownership attribution on repositories, images and services.
- Track fixes per asset with separate states for patched, mitigated (br rejected or auto-decode off) and no vendor fix.
- Verify runtime exposure after redeploy from the running image digest and Node version, not the merged pull request.
Download the campaign config. Import it into Phoenix to find every vulnerable Brotli, brotlicffi and Bun version across your assets.
Phoenix Security correlates vulnerable decoder copies with running workloads, finds internet-exposed and fetch-heavy services through attack surface management, and assigns each to an owner, shrinking a cross-ecosystem CVE into a backlog teams can close.
Frequently asked questions
What is HTTP2Shell (CVE-2026-111497)?
HTTP2Shell is CVE-2026-111497, an out-of-bounds write in Google’s Brotli C decoder, versions 1.0.3 through 1.2.0. A crafted Brotli stream corrupts decoder memory and can lead to a crash, a memory leak or remote code execution. GitHub rates it 9.2 Critical (CVSS 4.0), Node.js High and Red Hat Important.
Which versions fix CVE-2026-111497?
Brotli 1.0.10, 1.1.1 and 1.2.1; Node.js 22.23.4, 24.22.1 and 26.12.1; Bun 1.4.4; PyPI Brotli 1.2.1 and brotlicffi 1.2.1.0; .NET 8.0.33, 9.0.22 and 10.0.14; Envoy 1.40.0, 1.39.3, 1.38.6 and 1.37.8; Istio 1.29.9, 1.30.6 and 1.31.2; brotli4j 1.24.0; Ruby gem 0.9.1; PHP extension 0.21.1; Perl IO::Compress::Brotli 0.023. Node.js 20, 23 and 25 are end of life and get no fix.
Is .NET affected by CVE-2026-111497, and how do I fix it?
Yes. .NET 8, 9 and 10 bundle the vulnerable decoder, reached through ASP.NET Core request decompression, HttpClient automatic response decompression and the System.IO.Compression Brotli APIs. Decompression is off by default, so only apps that opted in are exposed. Microsoft shipped an out-of-band fix on October 25, 2026: upgrade to 8.0.33, 9.0.22 or 10.0.14. .NET Framework is not affected.
How do I turn off Brotli decoding without upgrading?
It depends on the stack. In curl, drop –compressed and leave CURLOPT_ACCEPT_ENCODING at NULL. In Python, uninstall Brotli and brotlicffi and drop httpx[brotli]. In Node.js, reject Content-Encoding: br request bodies at the proxy; fetch() has no switch, so upgrade the runtime. In Envoy, remove the Brotli decompressor filter. Each stack is independent, and WAF rules only cover inbound requests.
External References
- CVE record (cvelistV5, with CISA ADP SSVC)
- NVD
- GitHub Advisory GHSA-9v8h-qqgm-4m35
- PyPA PYSEC-2026-4322 (Brotli)
- PyPA PYSEC-2026-4323 (brotlicffi)
- Node.js October 2026 security releases
- Node.js v22.23.4 release notes
- google/brotli commits 0ed4268, 50a9b70, b418fa8, da98063
- google/brotli releases v1.0.10, v1.1.1, v1.2.1
- Envoy advisory GHSA-3wvj-j6qm-62mr
- ISTIO-SECURITY-2026-007
- Debian security tracker
- OpenSourceMalware, Node2Shell affects Python
- Imperva, Node2Shell from Brotli data to RCE
- The Hacker News, Brotli decoder flaw also affects Bun, Python and other software (24 Oct 2026)
- Patch the Planet
- Broccoli2Shell vendored-copy tracker
- Vendure, October 2026 Node.js releases
- Microsoft, Azure WAF for CVE-2026-111497
















