HTTP2Shell (CVE-2026-111497): One Brotli Bug, Patches Across Node.js, Python, .NET, PHP, and More

HTTP2Shell is not an HTTP/2 flaw. It is a memory corruption bug in Google's Brotli decoder, and it reaches any application that decompresses Brotli data it did not create. Which stacks are affected, the fixed version for each, how to find hidden copies of Brotli, and why updating one package is rarely enough.

Published 9 min read
Blue and grey network cables plugged into a numbered patch panel

Over the past week the same vulnerability has appeared under three names. Node.js disclosed it on 21 October as part of its security releases, Wiz called it Broccoli2Shell, and by 23 October most of the industry had settled on HTTP2Shell. All three refer to CVE-2026-111497, an out-of-bounds write in Google's Brotli decoder, rated 9.2 (critical) under CVSS 4.0. The name is misleading: the bug has nothing to do with the HTTP/2 protocol. What makes it serious is how many runtimes, libraries, and proxies quietly decompress Brotli on your behalf. Imperva has already shown code execution against an unpatched Node.js image, and public proof-of-concept code is circulating. If your applications talk HTTP, this is worth an afternoon this week.

What the Bug Actually Is

Brotli is the compression format behind the Content-Encoding: br header. Browsers, CDNs, and most HTTP clients support it because it compresses text better than gzip. The decoder is a small C library maintained by Google, and almost every ecosystem either links against it or bundles a copy.

The flaw sits in the streaming decoder. When it meets an invalid block switch in the compressed stream, it reports the problem as "I need more input" instead of "this data is broken". The decoder keeps waiting and keeps appending incoming bytes to an internal buffer that is only eight bytes long, writing past its end. Depending on the memory layout, the result is a crash, leaked memory, or, with enough care, code execution.

Affected versions are Brotli 1.0.3 through 1.2.0. Google released fixes in three lines on 21 October: 1.0.10, 1.1.1, and 1.2.1. The underlying code fix had actually been committed in February 2026 without a security label, which is why some vendors had it and most did not.

Two Ways In: Requests and Responses

Exploitation needs one thing: your application decompressing Brotli data that an attacker controls. That can happen in two directions, and most early advice only covered the first.

  • Inbound: a client sends a request body with Content-Encoding: br, and a framework, middleware, or proxy decompresses it before your code sees it. ASP.NET Core request decompression and the Envoy decompressor filter are examples.
  • Outbound: your application fetches a URL and the remote server answers with a Brotli-compressed response. Node.js fetch, Python requests and urllib3 (when Brotli is installed), .NET HttpClient with automatic decompression, and curl built with Brotli all decode it automatically. Webhook deliveries, link previews, crawlers, payment callbacks to third-party APIs, and "import from URL" features are all this kind of path.

The outbound path is the one teams miss. Researchers testing Python found that urllib3 with an unpatched Brotli package decoded a br response even when the request explicitly asked for Accept-Encoding: identity. A malicious server does not have to wait for permission to compress. Blocking Brotli requests at the WAF does nothing for this direction.

It does not even have to be HTTP. JFrog researchers reached the same decoder through FFmpeg by way of a crafted JPEG XL image, and through Apache Arrow with a 534-byte input that sent pyarrow into an endless loop. Anywhere Brotli data from outside is decoded counts.

Fixed Versions by Stack

The table below lists what had shipped by 27 October. Vendors sometimes backport the fix without changing to the upstream version number, so if your distribution lists a fixed package with an older-looking version, trust the advisory rather than the number.

StackFixed versionNotes
Brotli (C library)1.0.10, 1.1.1, 1.2.1Affected: 1.0.3 to 1.2.0
Node.jsv22.23.4, v24.22.1, v26.12.1Node.js 20 and older are end of life and get no fix
Bun1.4.4Binaries from bun build --compile must be rebuilt
Python Brotli / brotlicffi1.2.1 / 1.2.1.0Pulled in by requests, urllib3, httpx[brotli], aiohttp, Scrapy
.NET runtime8.0.33, 9.0.22, 10.0.14.NET Framework is not affected; self-contained apps must be republished
PowerShell7.4.22, 7.5.13, 7.6.9Ships its own .NET runtime
PHP for Windows8.5.12-1, 8.4.27-1, 8.3.36-1, 8.2.35-1Rebuilt packages; php -v still shows the old number
PHP brotli extension0.21.1Only if you installed it via PECL
Ruby brotli gem0.9.1
Java brotli4j1.24.0Bundles Brotli 1.2.1
Go andybalholm/brotliv1.2.7Pure Go port; affected before v1.2.7 according to Wiz
Envoy1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12Or disable the Brotli decompressor filter
Istio1.31.2, 1.30.6, 1.29.9Uses Envoy as its sidecar and gateway
Git for Windows2.56.0.windows.4Its bundled curl decodes Brotli
ClickHouse, DuckDB, Apache ArrowFixes merged or pendingNo patched release as of 26 October

Why Updating the System Package Is Not Enough

Running apt upgrade will fix the shared libbrotli on the host, and that matters for nginx with ngx_brotli, curl, and anything else that links dynamically. But many of the most exposed programs carry their own copy:

  • Node.js compiles Brotli into the node binary. Only a new Node.js release fixes it, and the old process keeps running the old code until it restarts.
  • Python wheels for Brotli and brotlicffi include the decoder. A clean OS does not help a virtualenv with Brotli==1.2.0 in it.
  • .NET self-contained and NativeAOT builds embed the runtime. Patching the machine-wide runtime leaves them untouched.
  • Container images freeze whatever was installed at build time. The tag node:24 you pulled last month is still 24.22.0 or older until you pull and rebuild.
  • Go and Bun produce single binaries. The fix arrives only when you rebuild with the patched dependency and redeploy.

How to Find Every Copy

  1. 1Check runtimes on every server and in every image: node -v, bun --version, dotnet --list-runtimes, pwsh --version. Run them inside the container, not on the host, and compare with the table above.
  2. 2Check system packages: dpkg -l '*brotli*' on Debian and Ubuntu, rpm -q brotli on RHEL and SUSE, apk info -v brotli on Alpine.
  3. 3Check language dependencies: pip show Brotli brotlicffi and pipdeptree -r -p Brotli for Python, gem list brotli for Ruby, go list -m all | grep brotli for Go, and search pom.xml or build.gradle for brotli4j. Search lockfiles too, because the package is usually a transitive dependency.
  4. 4Run your usual scanner against the image, not just the source. Trivy, Grype, pip-audit, and npm audit all know the CVE by now. Note that npm audit will not flag Node.js itself, since the bug lives in the runtime, not in a package.
  5. 5For vendored C code, search the source tree for BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH. That error code was added by the fix, so a copy of decode.c without it is old.
  6. 6Rebuild and redeploy, then restart every process, worker, queue consumer, and cron job that was running the old build. A patched file on disk does nothing for a process started yesterday.

If You Cannot Patch Today

Temporary measures help, but each one covers only part of the problem:

  • Reject requests with Content-Encoding: br at the load balancer or WAF. Cloudflare and Azure WAF have released rules for this, but they only see inbound traffic.
  • Stop accepting Brotli on outbound calls where you can. In Python, uninstalling Brotli and brotlicffi makes urllib3 and requests fall back to gzip. In Bun, decompress: false turns off automatic decoding per request.
  • In Envoy and Istio, disable the Brotli decompressor filter until the proxy is upgraded.
  • Watch for unexplained crashes and restarts in services that make outbound HTTP calls. A segfault in a Node.js or .NET process that has never crashed before deserves a look.

One warning: a crypto token called $HTTP2SHELL started circulating shortly after the name did. It has nothing to do with the fix. The only real remedy is updated software.

Where to Start

Wiz reported that 91% of the cloud environments it monitors contained at least one vulnerable resource, which matches what you would expect from a library this widespread. You do not need to fix everything at once. Start with public-facing services that make outbound HTTP calls to URLs users can influence, then API gateways and proxies, then internal services, and finally developer laptops and CI runners, which often run old Node.js and Git for Windows builds for months.

For most teams in Indonesia that means three concrete jobs this week: move every Node.js server and Docker image to v22.23.4 or v24.22.1, update Brotli in Python projects and rebuild their images, and apply the Debian, Ubuntu, or Alpine updates on servers that run nginx with Brotli enabled. If your company lacks the people to do this across many servers, it is exactly the kind of routine maintenance a managed service should already be handling.

Key takeaways

  • HTTP2Shell, Node2Shell, and Broccoli2Shell are the same bug: CVE-2026-111497, an out-of-bounds write in Brotli 1.0.3 to 1.2.0, fixed in 1.0.10, 1.1.1, and 1.2.1.
  • It is not an HTTP/2 flaw. Any code that decompresses Brotli from outside is exposed, including responses your application fetches from other servers.
  • Node.js (v22.23.4, v24.22.1, v26.12.1), Python Brotli 1.2.1, .NET (8.0.33, 9.0.22, 10.0.14), Bun 1.4.4, Envoy, Istio, and PHP for Windows all have fixed releases.
  • Updating the OS package is not enough for runtimes that bundle Brotli. Rebuild container images, republish self-contained apps, and restart every running process.
  • Blocking Brotli requests at the WAF only covers inbound traffic. Treat it as a stopgap and patch.
Share this article

Related articles

More articles on software development, AI, cloud, and infrastructure.

Rows of wooden library card catalogue drawers with metal handles and paper labelsSecurity

8 min read

phpMyAdmin and PHP2Shell: No New phpMyAdmin CVE, but Your Install Is Still Exposed

There is no new phpMyAdmin CVE this month, and no new phpMyAdmin version to install. Yet phpMyAdmin 5.2.3 was one of the applications shown to be taken over through PHP2Shell without a login. Where your phpMyAdmin gets its PHP, how to patch it on Debian, Docker, cPanel, Plesk, and Synology, a stopgap that costs almost nothing, and how to check whether it has already been used against you.

PHP source code on a dark editor screen, viewed at an angleSecurity

9 min read

Laravel Security Updates, October 2026: TokenGuard Auth Bypass, Livewire Fixes, and PHP2Shell

Laravel disclosed a critical authentication bypass in TokenGuard (CVE-2026-114509) and a data destruction bug on 19 October, Livewire fixed an authorization bypass in #[Authorize], and PHP itself needs patching for PHP2Shell. Which applications are affected, the versions to install, and an update routine that does not miss queue workers.

Let’s talk

Looking for a software development partner?

Tell us about your project, what you need to build, and the challenges you are facing. We can discuss the technical approach, scope, timeline, and estimated cost.

Start a conversation