HTTP2Shell: Brotli Flaw CVE-2026-111497 Has Outgrown Node.js — Emergency .NET Patches, Python and Bun Updates, and a Fix That Sat in the Source for Seven Months

HTTP2Shell: Brotli Flaw CVE-2026-111497 Has Outgrown Node.js — Emergency .NET Patches, Python and Bun Updates, and a Fix That Sat in the Source for Seven Months

HTTP2Shell: Brotli Flaw CVE-2026-111497 Has Outgrown Node.js — Emergency .NET Patches, Python and Bun Updates, and a Fix That Sat in the Source for Seven Months

The memory-corruption flaw at the bottom of Google's Brotli decoder started the week as a Node.js bug and ended it with its own second name. Tracked as CVE-2026-111497, the out-of-bounds write lets crafted compressed data overwrite past the end of a buffer — a crash, a memory leak, or attacker-controlled code execution, depending on the program doing the decompressing. Node.js and Google shipped the first fixes on October 21; Microsoft followed with out-of-band .NET security updates on October 25; and by the weekend researchers were calling the cross-ecosystem wave HTTP2Shell, after Imperva's Red Team demonstrated "Node2Shell" code execution on an unmodified Node.js build.

The reporting credit belongs to Khai Tran and Sylvie Mayer of OpenAI's Codex Security team, and the disclosure carries an uncomfortable detail: the change that closes the flaw was added to Brotli's source code on February 27, but no Brotli release carried it until October 21 — nobody knew it was a security bug. Severity ratings split by scorer: Google calls it 9.2 (critical, CVSS 4.0), Node.js calls it high, Red Hat rates it 8.1, and Microsoft scores it 8.1 for .NET. Because every affected program ships its own copy of the C decoder, each had to update that copy on its own — Node.js to 22.23.4, 24.22.1, and 26.12.1 (20.x and older are end-of-life and unfixed), Bun to 1.4.4, .NET to 8.0.33, 9.0.22, 10.0.14 plus .NET 11 RC3, the Python Brotli and brotlicffi packages to 1.2.1, and the Google C library itself to 1.0.10, 1.1.1, or 1.2.1. Envoy and Istio are affected where Brotli decompression is configured.

Exposure depends on what decodes attacker-controlled data. In Node.js and Bun, the built-in fetch() reading a Content-Encoding: br response is exposed, and so are Express 5 or body-parser 2 request bodies — but a plain Node HTTP server does not decode bodies by itself. In Python, the risk is the package being installed at all: requests, urllib3, httpx, and aiohttp will decode Brotli responses on their own. A .NET app is exposed only if it turns on Brotli in ASP.NET Core request decompression or HttpClient decompression — neither is default — or passes untrusted data to Brotli APIs. OpenAI describes the bug as working in both directions: an unpatched server that accepts compressed input can be taken over by a malicious client, and an unpatched client, even curl --compressed, by a malicious server. No in-the-wild exploitation has been reported — CISA's October 23 assessment recorded exploitation as "none" — and Imperva's exploit, run on Node.js 24.22.0 for Linux on ARM64, took several tries and has not been released.

The wave kept widening. JFrog reported on October 25 that the flaw also reaches FFmpeg — through the JPEG XL library's use of Brotli to unpack image metadata, a path that starts with a file rather than a web request — plus ClickHouse, which a single ~23 MB crafted request crashed, and Apache Arrow, whose C++ builds bundle an old Brotli and whose pyarrow fell into an endless loop on a 534-byte input. Debian, Amazon Linux 2023, and openSUSE shipped fixed packages; Ubuntu was still evaluating as of October 26. Dependency scanners are a weak signal here: the CVE record names Google's repository, and GitHub's advisory listed no affected package until the PYSEC entries for the two Python packages landed on October 23 — check installed versions manually.

What Should You Do?

  1. Update every runtime and library that decodes Brotli — Node.js 22.23.4/24.22.1/26.12.1, Bun 1.4.4, .NET 8.0.33/9.0.22/10.0.14, Python Brotli/brotlicffi 1.2.1, and any product bundling the C library.
  2. Restart services and rebuild images — a running process keeps the old decoder until it restarts; self-contained and NativeAOT .NET apps must be rebuilt and republished, and container images rebuilt from updated bases.
  3. Where you cannot patch yet, stop decoding untrusted Brotli — reject requests with Content-Encoding: br before any proxy or parser, disable automatic Brotli decoding in HTTP clients, and in Python uninstall Brotli/brotlicffi so clients fall back to gzip and deflate. Dropping br from Accept-Encoding alone does not help.
  4. Do not trust a green scan — verify installed Python package versions by hand, and remember file-based paths (Parquet, JPEG XL) never touch the HTTP edge where a blocking rule could catch them.

The WAF Angle

A compressed request body is opaque to a signature engine, and the HTTP2Shell wave makes that gap unusually concrete: the payload that corrupts memory is inside the very encoding your edge is told to pass through. The workable edge move is coarse and reliable — refuse Content-Encoding: br on inputs until runtimes are patched, which is exactly what Cloudflare's October 25 managed rules and Vercel's bulletin-level guidance push toward, and both note the limits (Cloudflare's new rules only log by default; Vercel's firewall does not cover responses your functions fetch from other servers). This is the second engine-level RCE story of the month on this site — the PHP2Shell emergency patches covered every PHP version back to 5.4, and that bug also lived below the framework layer where WAF rules reason. When the flaw is in the runtime — as with Next.js's out-of-band RCE fixes earlier this month — patch velocity is the control that actually closes the hole; edge filtering is the tourniquet you apply while it ships.

Sources