On this page
On 21 October 2026 Node.js released 22.23.4, 24.22.1 and 26.12.1 to fix an out-of-bounds write in the Brotli decoder it bundles, and Google released Brotli 1.2.1, 1.1.1 and 1.0.10 the same evening. The flaw is tracked as CVE-2026-111497. It was first nicknamed Node2Shell and is now widely called HTTP2Shell. Google, which assigned the CVE, rates it 9.2 under CVSS v4.0. No authentication is needed: the attacker only has to get a crafted Brotli stream decoded. Node.js says this “can lead to a crash, disclosure of process memory, or remote code execution”, and Imperva has shown code execution on the official Node.js image. The bug sits in a library, not a product, so the question is not which vendor you use but which of your services decode Brotli that someone else controls.
That is a long list. The same decoder is compiled into Node.js, Bun, the Python Brotli and brotlicffi packages, Envoy, the .NET runtime and the libbrotli package of most Linux distributions, and each ships its own fix on its own schedule. OpenAI, whose Codex Security team reported the bug, keeps the reference list of fixed releases and mitigations on its Patch the Planet page. The CVE record gives the simplest instruction: update “any Brotli libraries or binaries” between 1.0.3 and 1.2.0.
What to do, in order
- Find every runtime that decodes Brotli from the network. Start with internet-facing Node.js services, especially Express 5 APIs and anything that fetches user-supplied URLs (webhooks, link previews, image proxies, the Next.js image optimizer). Then Bun, Python services with
Brotliinstalled, Envoy and Istio gateways with a Brotli decompressor, .NET applications that enabled Brotli decompression, and desktop tools built on Electron, which can carry both a Chromium and a Node.js copy. Include container images: Node.js and Bun carry their own copy of the decoder, so a patched host does not patch the image. Compressing outgoing responses with Brotli does not count; only decoding does. - Update Node.js to 22.23.4, 24.22.1 or 26.12.1; Bun to 1.4.4; Python
Brotlito 1.2.1 andbrotlicffito 1.2.1.0; Envoy to 1.40.0, 1.39.3, 1.38.6 or 1.37.8; Istio to 1.31.2 or 1.30.6; .NET to 10.0.14, 9.0.22 or 8.0.33; and your distribution’sbrotlipackage. Then rebuild the images and restart every service, worker, scheduled job and replica: a library update on disk does nothing for a process that already loaded the old one. Node.js 20 and other end-of-life lines get no fix; move them to 22, 24 or 26. - If you cannot patch today, refuse Brotli from untrusted peers at the edge and in the application, knowing that this blocks the paths you know about, not the bug.
- Verify the version inside each running process and image, not the one the package manager reports on the host, and review logs from 6 October onwards, when the first fixed releases (Envoy) became public.
The vulnerability
| CVE-2026-111497 | |
|---|---|
| Upstream fix | Brotli 1.2.1, 1.1.1, 1.0.10, from commit 0ed4268 |
| Class | Out-of-bounds write (CWE-787) caused by an unchecked return value (CWE-252) |
| Trigger | A malformed Brotli stream, decompressed by streaming code that accepts more input |
| Impact confirmed | Memory corruption and memory disclosure (Node.js); code execution on Node.js 24.22.0 arm64 in Imperva’s lab |
| CVSS v4.0 | 9.2 (Google, CNA) |
How the bug works. Brotli splits compressed data into blocks and can switch between block types mid-stream. A crafted stream asks for a block switch that its own header says is impossible. The vulnerable decoder reported that error as “I need more input” instead of “this stream is invalid”, according to Node.js, so a streaming decoder kept appending incoming bytes to an eight-byte internal buffer and wrote past its end into neighboring decoder state. What happens next depends on what gets overwritten: a crash, a memory leak back to the attacker, or, with the right heap layout, code execution. The fix makes the decoder return a dedicated error (-17, invalid block switch) and stop.
Attacker-controlled Brotli reaches a decoder three ways:
- Request bodies sent with
Content-Encoding: br, decoded by the server before the application sees them. - Responses fetched from a server the attacker controls, decoded automatically by the HTTP client.
- Files that embed Brotli, such as WOFF2 fonts, Parquet data files or archives, opened by a parser.
The names. GitHub’s advisory still calls it “Node2Shell”, after the first runtime to ship a fix. When the same decoder turned up in Python packages, Aikido Security renamed it HTTP2Shell because “many libraries and applications using HTTP” are exposed. Despite the name, it is not an HTTP/2 bug and does not need HTTP at all. There is one CVE.
Who is exposed
The CVE record covers Brotli 1.0.3 up to the fixed releases, on all platforms. Whether a given service is exposed depends on whether it decodes Brotli it did not produce, and that differs by stack:
| Stack | What the vendors confirm | Priority |
|---|---|---|
| Node.js 22, 24, 26 (and end-of-life lines) | Decoder bundled in the binary and reached through node:zlib (Node.js). Express 5 decodes br request bodies by default (body-parser 2.x, inflate: true); fetch() decodes br responses. Imperva demonstrated code execution through both | Patch first |
| Bun up to 1.4.3 | Decoder reached by default from fetch() on any br response, and from node:zlib (Bun issue) | Patch first |
| Envoy, Istio | Exposed when the Brotli decompressor filter is configured (Envoy), in Istio typically through an EnvoyFilter (Istio) | Patch first if configured |
Python Brotli, brotlicffi | Fixed in 1.2.1 and 1.2.1.0 (GitHub). urllib3, requests, httpx and aiohttp decode br responses when one of them is installed (OpenSourceMalware) | Patch now |
| .NET 8, 9, 10 | Not reachable by default; exposed when ASP.NET Core request decompression or HttpClient automatic decompression includes Brotli, or code calls BrotliStream / BrotliDecoder on untrusted data. .NET Framework is not affected (Microsoft) | Patch now |
System libbrotli (curl, distro Node.js and Python, browsers) | Fixed through the distribution package, once processes restart | Patch now |
Versions. Node.js lines that are end of life, including 20.x, get no fix: the Node.js advisory states that end-of-life versions are always affected. The Bun issue confirms that every Bun release before the fix built a vulnerable Brotli; Bun 1.4.4 updates it to Brotli 1.2.1. Envoy 1.36.12 also contains the fix, but the 1.36 branch is no longer supported, and Istio 1.29.9 is the last release of a line that reached end of life on 15 October. PHP’s own Windows builds bundle Brotli too: the PHP project shipped 8.5.12-1, 8.4.27-1, 8.3.36-1 and 8.2.35-1 on 26 October, where the decoder is reachable through cURL when automatic response decompression is on (off by default). Third-party trackers such as Phoenix Security list more bundled copies still waiting for a release on 25 October, among them DuckDB, ClickHouse, Apache Arrow, 7-Zip forks and EDK II. Those reach the decoder through files rather than HTTP; we have not verified each entry. The Patch the Planet page gives the categories to check rather than a list: Electron-based tools (where “a fixed browser component does not establish that the embedded Node.js runtime is fixed”), curl and libcurl, Python and Java bindings, bundled wheels and static binaries, font decoders and data-file readers. It also notes that “using Brotli only to compress outgoing data does not by itself establish exposure”.
Distribution packages. A distribution update only fixes software that links the system library, and only after it restarts; Node.js and Bun binaries from upstream or from container images carry their own copy. Status on 26 October was uneven. Debian has fixed brotli in bookworm (DLA-4855-1) and trixie (DSA-6582-1). Ubuntu still lists brotli as needing evaluation on every release and marks only focal’s nodejs as vulnerable, because later releases link the system library. Red Hat lists RHEL 7 to 10 as affected, through brotli, the Python bindings, Node.js, Firefox, Thunderbird and JBoss Core Services, with errata still to come.
Exploitation status
As of 26 October 2026:
- Code execution has been demonstrated in a lab. Imperva ran its code inside the official, unmodified Node.js 24.22.0 Linux arm64 image, through both a
fetch()response and an Express request body. It “took multiple attempts”, and Imperva has not released the exploit. Vercel also reports researchers demonstrating memory disclosure and command execution on unpatched 22.x, 24.x and 26.x. - Public research disputes the impact. The brotli2crash repository, published on 22 October, argues that the vulnerable code path is “structurally unreachable from any Brotli stream, valid or malformed”, and that the practical impact is denial of service. That contradicts Imperva’s result. Red Hat says there is no public, reliable remote code execution proof of concept. We follow the vendors and Imperva: plan for code execution.
- No attacks have been reported. None of the vendors or researchers cited here had reported exploitation in the wild. CISA’s Known Exploited Vulnerabilities catalog, in its 23 October version, does not list the CVE.
- The fix has been public since February. The upstream commit landed on 27 February 2026 without a security label, and the CVE record gives that date as the public date. Envoy shipped fixed releases on 6 October and Istio on 15 October, more than two weeks before Node.js disclosed the bug. Node.js’s regression test now shows anyone where to look.
- Be careful with exploit code. Do not run proof-of-concept code from unknown sources on production or analyst machines; fake exploits are a common lure after a high-profile disclosure.
Our assessment: patch internet-facing Node.js and Bun services this week as an emergency change, starting with Express 5 APIs and anything that fetches URLs a user can choose. The exploit is hard, but it has been shown on a default image, and those services decode Brotli without anyone having turned it on. .NET and Envoy can follow in the normal cycle where Brotli decompression is confirmed off. The long tail is the real work: every container image, bundled runtime and file parser that carries its own copy of the decoder.
If you cannot patch today
The goal is to stop untrusted Brotli from reaching a vulnerable decoder while keeping gzip and deflate, which Patch the Planet says can stay on. Apply it to every decoding path; where a stack cannot block Brotli on its own, turn off automatic decompression or pause that path. Red Hat recommends that reverse proxies “strip or reject” Content-Encoding: br from untrusted clients until the library is updated. At an nginx edge, covering direct-to-origin and alternate routes too:
# CVE-2026-111497: reject Brotli-encoded request bodies before they reach Node.js,
# Bun, Envoy or .NET. Side effect: clients that legitimately send br bodies get 415.
if ($http_content_encoding ~* "br") {
return 415;
}
# This does not touch Accept-Encoding: br, so responses to browsers stay compressed.
Guard Express before any parser runs. Patch the Planet publishes a middleware that allows only uncompressed, gzip and deflate bodies and answers anything else, including stacked encodings such as
gzip, br, with 415. Register it before every JSON, form, raw, text and route-specific parser:// CVE-2026-111497: reject Brotli request bodies, keep gzip and deflate. // Source: patchtheplanet.org/brotli. Register before every body parser. app.use((req, res, next) => { const encoding = (req.get('Content-Encoding') || 'identity') .trim().toLowerCase(); if (!['identity', 'gzip', 'deflate'].includes(encoding)) { return res.status(415).send( 'Use gzip, deflate, or an uncompressed request body.' ); } next(); });If you cannot place it before decoding, set
inflate: falseon every body parser instead, which rejects all compressed bodies.Turn off Brotli in .NET. In ASP.NET Core, remove the
brprovider from an existingAddRequestDecompressioncall (Microsoft warns not to add the call if you do not already have it), and dropDecompressionMethods.Brotlifrom an existingAutomaticDecompressionsetting.Disable only the Brotli decompressor in Envoy and Istio. Remove the decompressor filter whose library is Brotli, or set
enabledtofalsefor bothrequest_direction_configandresponse_direction_configand remove anyruntime_keythat could turn it back on. In Istio, edit theEnvoyFilterthat inserts it, and do not remove everyenvoy.filters.http.decompressorfilter: gzip and zstd share the name. A disabled filter passes the compressed bytes on, so the services behind it still need protection.Contain outbound fetches. Request filtering does nothing for responses a service fetches itself, and removing
brfromAccept-Encoding“does not stop a server from sending it”. Patch the Planet’s Python example reads with urllib3preload_content=Falseanddecode_content=False, checksContent-Encoding, and only then decodes; it warns that checking headers after a normalfetch()or automatic body read “can be too late”. UninstallingBrotliandbrotlicffialso makes urllib3, requests and aiohttp fall back to gzip and deflate, according to OpenSourceMalware. For webhooks, link previews and image proxies, an egress proxy that rejectsbrresponses covers every runtime at once.Turn off Next.js image optimization. Vercel notes that Next.js “does not block Brotli responses in image optimization”. Until Node.js is patched, Patch the Planet recommends
images: { unoptimized: true }innext.config.js, then a rebuild and a check that/_next/imageno longer runs the optimizer. It does not cover other server-side fetches or custom loaders.Pause untrusted files. Fonts, data files and archives from outside sources wait until the parser that reads them is patched.
Check what your WAF actually does. Cloudflare’s 25 October rules match
Content-Encoding: brrequests and Next.js/_next/imagetraffic, but they ship in Log mode: switch the Brotli rule to Block if you want it to block anything. Imperva and Vercel have deployed blocking rules for their customers. Turning off Brotli response compression on a CDN or web server changes nothing here: it does not disable request or client-side decompression.
WAF rules and request-size limits see requests, not the responses your servers fetch. After any temporary block, test that gzip and other permitted encodings still work. Mitigations and signatures buy time; they do not change the conclusion.
Detection and verification
Check the version that serves requests. Run node -v inside each container and on each host, not just on the build machine: a host package inventory misses the Node.js binary inside an image. Do the same with bun --version, dotnet --list-runtimes, pip show Brotli brotlicffi and envoy --version. For the system library, go by your distribution’s package version (dpkg -l libbrotli1, rpm -q brotli), not upstream version numbers, then restart everything that loaded it. To test a binary directly, Node.js’s regression vector from test/parallel/test-zlib-invalid-input.js makes a patched decoder fail early with error -17; the Bun issue shows unpatched builds failing later with -9.
Look from the outside. Nothing in a response reveals which Brotli decoder a server runs, so external checks find the stack, not the bug. Express announces itself with X-Powered-By: Express by default, Envoy with server: envoy, and Next.js through /_next/ paths. That gives a first list of candidates to check internally. Remove those headers once you are done (app.disable('x-powered-by') in Express).
Hunt. No vendor had published indicators of compromise at the time of writing. Imperva suggests using crashes and decoder errors to prioritize, while warning that “neither is a reliable stand-alone sign of exploitation”. Review from 6 October onwards, and with more attention from 21 October, when the regression test became public:
- Requests with
Content-Encoding: brto services that never receive compressed bodies, or from clients that never sent them before. Cloudflare’s log-mode rule records exactly this. - Node.js, Bun or Envoy processes that exit with
SIGSEGV(exit code 139) or abort during decompression, and bursts of Brotli decoder errors in application logs. - Outbound requests from image optimizers, webhook senders or link previewers to hosts you do not recognize, especially through
/_next/image?url=with a remote URL. - After any crash: child processes spawned by the runtime, new files in the application directory or
/tmp, and outbound connections from web workers.
Why the score alone will not rank this
Google scores CVE-2026-111497 9.2 with AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H. The AT:P already records that something must be in place first: a decoder that accepts attacker data. Everyone downstream weighed that differently. Node.js rates it High. Red Hat and Microsoft give it 8.1 under CVSS v3.1 with high attack complexity. Ubuntu sets its priority to Medium, and Debian tags the Node.js and Python packages unimportant because they use the system library. None of those numbers says whether your Express 5 API on Node.js 24 decodes br by default (it does) or whether your .NET service turned Brotli decompression on (by default it did not). Ranked by CVSS, every one of them lands in the same bucket. Ranked by what is internet-reachable, which runtime it uses and what it decodes, the order of work becomes obvious. Our CVSS vs EPSS page develops that argument in general.
Panop’s discovery starts from a company name, finds the services you expose and fingerprints what they run. For an advisory like this one, the first step above becomes a filter on an inventory you already have, not a search under time pressure. Request a scan to see where Node.js, Envoy and .NET answer on your perimeter.
Timeline
| Date | Event |
|---|---|
| 27 Feb 2026 | Google lands the fix (commit 0ed4268) in the Brotli repository, with no release or security label. |
| 6 Oct 2026 | Envoy publishes 1.39.3, 1.38.6, 1.37.8 and 1.36.12, which contain the fix (1.40.0 follows on 13 October). |
| 15 Oct 2026 | Istio publishes 1.31.2, 1.30.6 and 1.29.9 with bulletin ISTIO-SECURITY-2026-007, which now lists the CVE. |
| 21 Oct 2026 | Node.js publishes 22.23.4, 24.22.1 and 26.12.1 with its security release. Google releases Brotli 1.2.1, 1.1.1 and 1.0.10. |
| 22 Oct 2026 | Google publishes the CVE record. Imperva reports code execution on Node.js 24.22.0. PyPI Brotli 1.2.1 and Bun 1.4.4 ship. brotli2crash disputes the RCE impact. |
| 23 Oct 2026 | GitHub extends its advisory to the PyPI packages; brotlicffi 1.2.1.0 ships. Envoy publishes its advisory. Ubuntu publishes its CVE page. OpenAI publishes the latest update of its Patch the Planet page. CISA’s Known Exploited Vulnerabilities catalog update does not list the CVE. |
| 25 Oct 2026 | Microsoft ships out-of-band .NET 8.0.33, 9.0.22 and 10.0.14. Cloudflare adds log-mode WAF rules. |
| 26 Oct 2026 | Red Hat publishes RHSB-2026-015. PHP ships updated Windows builds bundling Brotli 1.2.1. |
This advisory reflects public information as of 26 October 2026.
Sources
- Brotli decoder vulnerability (CVE-2026-111497), updated 23 October 2026 OpenAI (Patch the Planet)
- Brotli v1.2.1 release notes, 21 October 2026 Google
- CVE-2026-111497: Out-of-Bounds Write in Google Brotli Decoder CVE.org (Google CNA)
- Brotli commit 0ed4268: Detect and report invalid stream earlier, 27 February 2026 Google
- October 2026 security releases: Brotli decoder out-of-bounds write (CVE-2026-111497) Node.js
- GHSA-9v8h-qqgm-4m35: Out-of-bounds write in the Brotli decoder ("Node2Shell") also affects the PyPI Brotli package GitHub
- GHSA-3wvj-j6qm-62mr: Out-of-Bounds Write in Brotli Decompressor Envoy
- ISTIO-SECURITY-2026-007 Istio
- Microsoft Security Advisory CVE-2026-111497, 25 October 2026 Microsoft
- Brotli decoder is missing the CVE-2026-111497 fix (issue #46352) Bun
- Bun v1.4.4 release, 22 October 2026 Bun
- Updated PHP Windows builds for Brotli security fix, 26 October 2026 PHP
- body-parser README Express
- RHSB-2026-015: Out-of-Bounds Write, Google Brotli Decoder (CVE-2026-111497) Red Hat
- CVE-2026-111497 Red Hat
- CVE-2026-111497 Debian
- CVE-2026-111497 Ubuntu
- Known Exploited Vulnerabilities catalog CISA
- Node2Shell: From Brotli Data to Remote Code Execution, 22 October 2026 Imperva
- brotli2crash: Deep Analysis & Structural Unreachability Proof Qi Deng (GitHub)
- Node2Shell affects Python OpenSourceMalware
- Node.js security advisory: Brotli decoder RCE (CVE-2026-111497) Vercel
- WAF Release 2026-10-25, Emergency, Cloudflare Managed Ruleset Cloudflare
- HTTP2Shell Expansion (CVE-2026-111497): Where the Brotli Decoder Bug Went After Node.js and Python Phoenix Security
- CVE-2026-111497 has evolved into HTTP2Shell Aikido Security (X)