HTTP2Shell (CVE-2026-111497): How a Brotli Decoder RCE Spread from Node2Shell to Python2Shell and Every HTTP Stack

HTTP2Shell, CVE-2026-111497: the same vulnerable Brotli decoder hidden inside Node.js, Bun, Python, Envoy, .NET and cdxgen, with malicious Brotli data entering from both sides.

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.

Executive 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 isOut-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 bitesBrotli 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 mattersUnauthenticated 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 statusPartial, 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 actionUpgrade 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.

ComponentAffectedFixed inAction
Google Brotli (C library)1.0.3 – 1.2.01.0.10, 1.1.1, 1.2.1Patch OS package or vendored copy
Node.js 22 / 24 / 26< 22.23.4 / < 24.22.1 / < 26.12.122.23.4, 24.22.1, 26.12.1Upgrade runtime, rebuild, restart
Node.js 20 / 23 / 25allnone (EOL)Migrate to 22, 24 or 26
Bun1.0.21 – 1.4.31.4.4Upgrade; rebuild compiled binaries
PyPI Brotli / brotlicffi< 1.2.1 / < 1.2.1.01.2.1 / 1.2.1.0Upgrade; audit vendored wheels
.NET 8 / 9 / 10< 8.0.33 / < 9.0.22 / < 10.0.148.0.33, 9.0.22, 10.0.14Upgrade runtime (Framework not affected)
Envoy1.39.2, 1.38.5, 1.37.7, 1.36.121.40.0, 1.39.3, 1.38.6, 1.37.8Upgrade (if Brotli decompressor on)
Istio1.30.0-1.30.5, 1.31.0-1.31.11.30.6, 1.31.2Upgrade
brotli4j (Java)< 1.24.01.24.0Upgrade
Ruby brotli / PHP ext< 0.9.1 / < 0.21.10.9.1 / 0.21.1Upgrade
Perl IO::Compress::Brotli< 0.0230.023Upgrade (CVE-2026-115936)
Go andybalholm/brotli< v1.2.7v1.2.7Upgrade (crash-only)
Debian 12 / 13 brotli1.0.9-2 / 1.1.0-21.0.9-2+deb12u1 / 1.1.0-2+deb13u1apt 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 Windowsnone yetInventory, mitigate, watch vendor
HTTP2Shell (CVE-2026-111497) live tracker as of October 26, 2026: 38 of 80 tracked products fixed, 12 with a fix in progress and 19 still vulnerable. Day-by-day status for upstream Brotli, Node.js, Bun, Electron, GraalJS, Deno and Python packages.
Snapshot of the HTTP2Shell live tracker, October 26, 2026. See the live version below for today’s status.

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

FieldValue
NamesHTTP2Shell, Node2Shell, Python2Shell, Broccoli2Shell (one bug)
VendorGoogle (CNA and upstream); redistributed by Node.js, Bun, PyPI maintainers, Envoy, Microsoft and others
ProductBrotli reference C decoder, c/dec/decode.c
Vulnerability typeOut-of-bounds write, heap memory corruption (CAPEC-100)
CWECWE-787 Out-of-bounds Write; CWE-252 Unchecked Return Value
CVSS4.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
IdentifiersCVE-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 byKhai Tran and Sylvie Mayer, OpenAI Codex Security. Node.js fix by Matteo Collina and Antoine du Hamel
Patch availableYes upstream and in major runtimes; no for many vendored copies
Active exploitationNot 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 KEVNot 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

  1. 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.
  2. 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.
  3. 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 FebUpstream commit 0ed4268 lands without a security label
21 OctNode.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 OctCVE 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 Octbrotlicffi 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 OctBun 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 OctCloudflare 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

PackageVulnerable VersionsFixed VersionNotes
Google Brotli (C)1.0.3 to 1.0.9, 1.1.0, 1.2.01.0.10, 1.1.1, 1.2.1CVE record ranges; source of every other copy
Node.js 22.x< 22.23.422.23.4Bundled 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.124.22.1Imperva RCE shown on 24.22.0 arm64
Node.js 26.x< 26.12.126.12.1
Node.js 18.x, 20.x, 23.x, 25.xAllNoneEnd of life
Bun1.0.21 to 1.4.31.4.4Rebuild bun build –compile executables
.NET 8 / 9 / 10< 8.0.33 / < 9.0.22 / < 10.0.148.0.33, 9.0.22, 10.0.14; .NET 11 RC3Out-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 Brotli1.0.4, 1.0.6, 1.0.7, 1.0.8, 1.0.9, 1.1.0, 1.2.01.2.1PYSEC-2026-4322; released versions in range per PyPI
PyPI brotlicffi1.0.9.0 to 1.0.9.2, 1.1.0.0, 1.2.0.0 to 1.2.0.21.2.1.0PYSEC-2026-4323
Envoy1.39.2, 1.38.5, 1.37.7, 1.36.11 and earlier1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12Only with the Brotli decompressor configured
Istio1.31.0 to 1.31.1, 1.30.0 to 1.30.5, 1.29.0 to 1.29.81.31.2, 1.30.6, 1.29.9Only with Brotli decompression enabled; 1.29 is end of life
cdxgen standalone binaries and images13.4.0 and earlier, 12.8.8 and earlier13.5.0, 12.8.9npm install uses system Node instead
Debian brotlibookworm, trixie1.0.9-2+deb12u1, 1.1.0-2+deb13u1DSA-6582-1. Backports keep the old upstream version string; sid unfixed
Ubuntu brotli24.04libbrotli1 1.1.0-2ubuntu0.1Other releases per the Ubuntu tracker
Alpine 3.24 brotli< 1.2.1-r01.2.1-r0
Go andybalholm/brotliBefore v1.2.7v1.2.7Crash only
brotli4j (Java)< 1.24.01.24.0
Perl IO::Compress::Brotli< 0.0230.023CVE-2026-115936
Ruby brotli gem, PHP brotli extension, UEFITool, MuPDFBundled copies0.9.1, 0.21.1, A76, 1.28.0Fixed 24 October; MuPDF 1.27.x still affected
Chromium, Firefox, ngx_brotli, Ghostscript, SumatraPDFVendor-specificReported fixedElectron apps carry Chromium and Node copies
curl for Windows, VS Code ServerBundled copiesVendor updatesReported fixed

Not fixed yet

PackageVulnerable VersionsFixed VersionNotes
DuckDB, ClickHouseCurrentPending (PR #27791, PR #127419)Parquet input
Apache Arrow bundled buildBundles 1.0.9NoneParquet input
libjxl, NanaZip, 7-Zip-zstd, p7zipCurrentNoneFile-format input
EDK IICurrentNoneFirmware images
woff2 (Makefile build)Bundles 1.0.3NoneWOFF2 fonts; the CMake build links the system library
GraalJS / v8-jsi, YDBCurrentNone
Pillow, pyarrow, curl-cffi, pycurlVendored decoder in wheelsNoneUpgrading Brotli alone does not fix them
Git for WindowsBundled copyNoneWatch the vendor
Debian nodejsCurrentPendingDistro 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

EnvironmentRisk LevelReason
Internet-facing Node.js APIs on Express 5 / body-parser 2.xCriticalexpress.json() inflates br bodies by default; unauthenticated reach
Services fetching attacker-chosen URLs (webhooks, link previews, URL import, image proxies, OIDC discovery)CriticalHostile server returns a br response; inbound WAF rules never see it
Browsing AI agents and crawlersCriticalEvery fetched page is attacker input to an auto-decoding client
Envoy / Istio with the Brotli decompressorHighProxy decodes before application controls apply
CI/CD runners, build tools, SBOM generators (cdxgen binaries)HighScan and download untrusted input; runners hold tokens and signing keys
Data pipelines (DuckDB, Arrow, ClickHouse) on third-party ParquetHighFile-borne trigger, no fix yet
.NET services with Brotli decompression enabledMediumFixed October 25; decompression is off by default, so only services that opted in are exposed
Developer workstations, Electron appsMediumTwo embedded copies per Electron app; local secrets
Apache / Nginx default configsLowEncoder 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

SignalWhereMeaning
Content-Encoding containing br on requests (any case, including gzip, br)Edge proxy, WAF, API gateway logsInbound attempts; legitimate clients rarely send Brotli bodies to JSON APIs
BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH or error -17Application logs on patched Node.js and Brotli 1.2.1Malformed block switch rejected; likely a probe
SIGSEGV / SIGABRT or heap-corruption aborts in node, bun, python, envoyContainer restarts, journald, crash reportersPossible trigger on an unpatched host, especially after a br request or outbound fetch
Outbound fetches to new or user-supplied hosts answering Content-Encoding: brEgress proxy logsOutbound path; correlate with webhook, preview and import features
Parquet, JPEG XL, WOFF2 or PDF uploads followed by worker crashesIngestion logsFile-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

  1. 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.
  2. 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].
  3. 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.
  4. Search proxy config for envoy.compression.brotli.decompressor, including Istio EnvoyFilter manifests.
  5. Map every outbound fetch feature to its runtime and HTTP client and mark it reachable or not.
  6. Regenerate SBOMs from built images rather than source manifests and diff them against the tables above.

Remediation Guidance

Immediate actions

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Upgrade cdxgen standalone binaries and images to 12.8.9 or 13.5.0.
  6. Move workloads off Node.js 18.x, 20.x, 23.x and 25.x.
  7. 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.
  8. 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:

  1. 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.
  2. Assign owners automatically through vulnerability ownership attribution on repositories, images and services.
  3. Track fixes per asset with separate states for patched, mitigated (br rejected or auto-decode off) and no vendor fix.
  4. 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

  1. CVE record (cvelistV5, with CISA ADP SSVC)
  2. NVD
  3. GitHub Advisory GHSA-9v8h-qqgm-4m35
  4. PyPA PYSEC-2026-4322 (Brotli)
  5. PyPA PYSEC-2026-4323 (brotlicffi)
  6. Node.js October 2026 security releases
  7. Node.js v22.23.4 release notes
  8. google/brotli commits 0ed4268, 50a9b70, b418fa8, da98063
  9. google/brotli releases v1.0.10, v1.1.1, v1.2.1
  10. Envoy advisory GHSA-3wvj-j6qm-62mr
  11. ISTIO-SECURITY-2026-007
  12. Debian security tracker
  13. OpenSourceMalware, Node2Shell affects Python
  14. Imperva, Node2Shell from Brotli data to RCE
  15. The Hacker News, Brotli decoder flaw also affects Bun, Python and other software (24 Oct 2026)
  16. Patch the Planet
  17. Broccoli2Shell vendored-copy tracker
  18. Vendure, October 2026 Node.js releases
  19. Microsoft, Azure WAF for CVE-2026-111497
Sarah is a cybersecurity researcher and product security specialist with over a decade of experience helping organisations identify and remediate vulnerabilities at scale. With a background in software engineering and threat intelligence, she has worked with Fortune 500 companies across financial services and healthcare sectors. Sarah is passionate about making security accessible and actionable for development teams.

Discuss this blog with our community on Slack

Join our AppSec Phoenix community on Slack to discuss this blog and other news with our professional security team

From our Blog

CVE-2026-119618 (HTTP2Reset) lets one unauthenticated HTTP/2 request corrupt the heap of Apache HTTP Server 2.4.55 to 2.4.70. Public labs crash workers on demand, and attacker bytes reach a dereferenced pointer. The fix in 2.4.71 is one line, but only servers that actually speak HTTP/2 are reachable.
Daniel Reeves
Frontier models made vulnerability discovery cheap: 948 Linux privilege escalations at about $1,280 each. Fixing didn’t speed up. Only 1 in 8 disclosed AI findings has a known upstream patch, and AI-found bugs are exploited at 1.3%, the historical norm. The bottleneck is verified remediation, owned by a team and proven by the next scan.
Francesco Cipollone
Derek

Derek Fisher

Head of product security at a global fintech

Derek Fisher – Head of product security at a global fintech. Speaker, instructor, and author in application security.

Derek is an award winning author of a children’s book series in cybersecurity as well as the author of “The Application Security Handbook.” He is a university instructor at Temple University where he teaches software development security to undergraduate and graduate students. He is a speaker on topics in the cybersecurity space and has led teams, large and small, at organizations in the healthcare and financial industries. He has built and matured information security teams as well as implemented organizational information security strategies to reduce the organizations risk.

Derek got his start in the hardware engineering space where he learned about designing circuits and building assemblies for commercial and military applications. He later pursued a computer science degree in order to advance a career in software development. This is where Derek was introduced to cybersecurity and soon caught the bug. He found a mentor to help him grow in cybersecurity and then pursued a graduate degree in the subject.

Since then Derek has worked in the product security space as an architect and leader. He has led teams to deliver more secure software in organizations from multiple industries. His focus has been to raise the security awareness of the engineering organization while maintaining a practice of secure code development, delivery, and operations.

In his role, Jeevan handles a range of tasks, from architecting security solutions to collaborating with Engineering Leadership to address security vulnerabilities at scale and embed security into the fabric of the organization.

Jeevan Singh

Jeevan Singh

Founder of Manicode Security

Jeevan Singh is the Director of Security Engineering at Rippling, with a background spanning various Engineering and Security leadership roles over the course of his career. He’s dedicated to the integration of security practices into software development, working to create a security-aware culture within organizations and imparting security best practices to the team.
In his role, Jeevan handles a range of tasks, from architecting security solutions to collaborating with Engineering Leadership to address security vulnerabilities at scale and embed security into the fabric of the organization.

James

James Berthoty

Founder of Latio Tech

James Berthoty has over ten years of experience across product and security domains. He founded Latio Tech to help companies find the right security tools for their needs without vendor bias.

christophe

Christophe Parisel

Senior Cloud Security Architect

Senior Cloud Security Architect

Chris

Chris Romeo

Co-Founder
Security Journey

Chris Romeo is a leading voice and thinker in application security, threat modeling, and security champions and the CEO of Devici and General Partner at Kerr Ventures. Chris hosts the award-winning “Application Security Podcast,” “The Security Table,” and “The Threat Modeling Podcast” and is a highly rated industry speaker and trainer, featured at the RSA Conference, the AppSec Village @ DefCon, OWASP Global AppSec, ISC2 Security Congress, InfoSec World and All Day DevOps. Chris founded Security Journey, a security education company, leading to an exit in 2022. Chris was the Chief Security Advocate at Cisco, spreading security knowledge through education and champion programs. Chris has twenty-six years of security experience, holding positions across the gamut, including application security, security engineering, incident response, and various Executive roles. Chris holds the CISSP and CSSLP certifications.

jim

Jim Manico

Founder of Manicode Security

Jim Manico is the founder of Manicode Security, where he trains software developers on secure coding and security engineering. Jim is also the founder of Brakeman Security, Inc. and an investor/advisor for Signal Sciences. He is the author of Iron-Clad Java: Building Secure Web Applications (McGraw-Hill), a frequent speaker on secure software practices, and a member of the JavaOne Rockstar speaker community. Jim is also a volunteer for and former board member of the OWASP foundation.

Join our Mailing list!

Get all the latest news, exclusive deals, and feature updates.

The IKIGAI concept
Protected By
Shield Security PRO