Guide
Node2Shell beyond Node.js: Brotli CVE-2026-111497 in Go, Java, .NET, PHP, Ruby, Linux and proxies
By Aman Singh · Published · 12 min read
CVE-2026-111497, better known as Node2Shell or HTTP2Shell and scored 9.2 out of 10 (Critical, CVSS 4.0), is usually described as a Node.js bug. But the code that is broken is Google's Brotli C decoder, versions 1.0.3 to 1.2.0. That decoder is copied into language runtimes, packages, proxies and every major Linux distribution. Each copy needs its own update, and as of 24 October 2026 several popular ones still had none.
The attack works in both directions: a malicious client can take over an unpatched server by sending it a Brotli-compressed request, and a malicious server can take over an unpatched client by sending it a Brotli-compressed response.
This guide lists every component I could confirm from its own repository, advisory or binary: what is affected, what is fixed, how to check, and what to do where there is no fix yet. Where I could, I tested the binaries themselves. The Node.js and Python details have their own guides: Node.js and Python.
On Node.js? Check and patch Node.js (Node2Shell) →
Why one bug reaches so many languages
Brotli is the br compression used across the web. Most languages do not reimplement it; they wrap Google's C code, either by bundling a copy inside their own binary or by loading the system's libbrotlidec. The bug is a missing check: when a stream asks for a "block switch" it is not allowed to make, the old decoder treated it as "need more input", kept going, and could write past an internal buffer. The CVE record lists memory corruption, data disclosure, crashes and possible code execution. Google released 1.2.1, 1.1.1 and 1.0.10 on 21 October 2026; the fix itself had been in the source tree since February without being treated as a security issue.
Separate reimplementations are a different case. The Go port had the same logic error but, being memory-safe Go, it can only crash; the Rust port and Google's own Java, JavaScript and C# ports are separate code and are not reported as vulnerable to the code-execution bug.
Every affected component, in one table
| Component | Brotli source | Status, 24 October 2026 | Do this |
|---|---|---|---|
| Google Brotli (C) | Itself | Fixed: 1.2.1, 1.1.1, 1.0.10 | Update, or apply the patch to vendored copies |
| Node.js | Bundled | Fixed: 26.12.1, 24.22.1, 22.23.4 | Update; 20.x and older get no fix |
| Bun | Bundled | Fixed: 1.4.4 | Update; rebuild compiled executables |
| Deno | Rust port | Not the C decoder | Nothing for this CVE |
| Electron | Bundled Node.js | Not fixed: 45.0.0, 44.9.3, 43.8.3 ship Node.js 24.22.0 | Watch for a release with Node.js 24.22.1 |
| Python brotli, brotlicffi | Bundled | Fixed: 1.2.1, 1.2.1.0 | Update and pin |
| pyarrow, Pillow (wheels) | Bundled | Not fixed | Avoid untrusted Brotli Parquet and WOFF2 fonts |
| Go (andybalholm/brotli) | Go port | Crash only; fixed in v1.2.7 | go get github.com/andybalholm/brotli@v1.2.7 |
| Rust (brotli-decompressor) | Rust port | Not the C decoder | Nothing for this CVE |
| Java: Brotli4j | Bundled (JNI) | Not released: fixed in source on 23 Oct | Watch for a release after 1.23.0 |
| Java: org.brotli:dec | Java port | Not the C decoder | Nothing for this CVE |
| .NET | Bundled 1.1.0 | Not fixed | Avoid decoding untrusted Brotli; watch .NET updates |
| PHP ext-brotli | Bundled 1.2.0 or system | Not fixed (PR open) | Build against a fixed system library |
| Ruby brotli gem | Bundled 1.2.0 | Not fixed (issue open) | Avoid decoding untrusted Brotli |
| Envoy | Bundled | Fixed: 1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12 | Update if the Brotli decompressor is configured |
| Istio | Envoy | Fixed: 1.31.2, 1.30.6, 1.29.9 | Update the control plane and proxies |
| curl / libcurl | System or bundled | Follows the library it uses | Update the OS package or the curl build |
| Git for Windows | Bundled | Not fixed in 2.56.0.windows.3 | Watch for a newer release |
| Debian | System | Fixed: DSA-6582-1, DLA-4855-1 | apt upgrade, restart services |
| Alpine | System | Fixed in 3.21 to 3.24 | apk upgrade brotli-libs |
| Ubuntu | System | Needs triage on every release | Watch for a USN |
| RHEL 8, 9, 10 | System | Affected, no fix yet (Important) | Watch for an RHSA |
| Amazon Linux 2023 | System | Pending fix | Watch ALAS |
Runtimes: Node.js, Bun, Deno, Electron
Node.js bundles Brotli and decodes it in more places than most people expect: Express 5 request bodies, fetch() and axios responses. The Node.js guide has a checker that detects the fix in the binary, plus stop-gaps. Distribution builds of Node.js (Debian's, for example) load the system library instead, so the OS package is what fixes them.
Bun 1.4.4, released 22 October, updates Brotli. Apps built with bun build --compile carry the runtime inside the executable, so rebuild them. Deno uses the Rust Brotli crate for its Node.js compatibility layer, not the C library.
Electron is the gap. Its three stable releases from 21 October, 45.0.0, 44.9.3 and 43.8.3, all bundle Node.js 24.22.0, the last unpatched build. Until a release with Node.js 24.22.1 or later is out, keep untrusted Brotli data away from the main process: do not decode user-supplied br files with zlib, and be careful with fetch() to servers you do not control.
Python, Go, Rust, Java, .NET, PHP, Ruby
Python
Update brotli to 1.2.1 and brotlicffi to 1.2.1.0. The harder part is the bundled copies: in tests, pyarrow 26.0.0 and Pillow 13.0.0 (Linux and macOS wheels) carried unpatched decoders that pip-audit did not flag, and the aiohttp web server decoded Brotli request bodies before middleware ran. The Python guide covers all of it, with a checker.
Go
The widely used github.com/andybalholm/brotli is a pure-Go translation of the C code. It had the same logic error, but Go's bounds checks turn it into a panic rather than memory corruption. It was fixed on 23 October in v1.2.7. A panic in a request handler can still take down a server that does not recover from it, so update anyway. Programs that use cgo bindings to the C library follow the system library instead.
Rust
brotli-decompressor (used by the brotli crate, reqwest, Deno and others) is a separate Rust implementation and is not the code this CVE is about. A Rust crate that links the C library through FFI is a different matter: check what it links.
Java
Brotli4j bundles the C library as a native JNI library. Its current release, 1.23.0, contains the unpatched decoder (the checker flagged its Linux native library); the project moved to Brotli 1.2.1 on 23 October, but no new release was on Maven Central as of 24 October. Netty uses Brotli4j to decode br when it is on the classpath. Google's pure-Java decoder, org.brotli:dec (used by OkHttp's Brotli support), is a separate port.
.NET
.NET bundles its own Brotli in System.IO.Compression, and the runtime repository still pins Brotli 1.1.0. No .NET update had addressed this CVE as of 24 October. I tested where it is reachable:
.NET 9.0.1
no default HttpClientHandler sent Accept-Encoding: (none)
DECODES BROTLI AutomaticDecompression = All sent Accept-Encoding: gzip, deflate, br- HttpClient does not decode Brotli by default. Code that sets
AutomaticDecompression = DecompressionMethods.Allor.Brotlidoes, so look for it in your HttpClient setup. - ASP.NET Core only decodes Brotli request bodies if you add the request decompression middleware (
UseRequestDecompression). If you do not need compressed request bodies, remove it until .NET is patched. - BrotliStream and BrotliDecoder used directly on uploaded or downloaded data are the remaining path.
PHP
The brotli PHP extension (kjdev/php-ext-brotli) bundles Brotli 1.2.0 by default, and its update to 1.2.1 was still an open pull request on 24 October. It can also be built against the system library, which is the way to get a fix today on Debian or Alpine. More PHP apps meet Brotli through libcurl: Guzzle and Symfony HttpClient use curl, which decodes br when libcurl was built with Brotli. Check with curl_version(): on the XAMPP PHP 8.0 build I tested, brotli_version was empty, so it cannot decode Brotli at all. For this month's PHP core fixes, see the PHP2Shell guide.
Ruby
The brotli gem vendors Brotli 1.2.0, and the issue tracking the update was open on 24 October. Until a fixed gem is out, avoid calling Brotli.inflate on data from outside.
Proxies and servers: Envoy, Istio, nginx, curl
- Envoy published GHSA-3wvj-j6qm-62mr on 23 October: only deployments with the Brotli decompressor filter configured are affected, fixed in 1.40.0, 1.39.3, 1.38.6, 1.37.8 and 1.36.12.
- Istio shipped the Envoy fix in 1.31.2, 1.30.6 and 1.29.9 (bulletin ISTIO-SECURITY-2026-007, 15 October).
- nginx and Apache use Brotli to compress responses (ngx_brotli, mod_brotli) and do not decode request bodies with it by default.
- curl only decodes Brotli when asked to. In a test against a server that always sends Brotli, the CLI returned raw bytes until I added
--compressed, which offersdeflate, gzip, br, zstd. In programs, the equivalent is settingCURLOPT_ACCEPT_ENCODING.
$ curl -s https://example.test/ | head -c 8 # raw bytes: curl did not decode
$ curl -s --compressed https://example.test/ # decoded: sends "deflate, gzip, br, zstd"Linux distributions and containers
Anything that loads the system libbrotlidec is fixed by one package update and a restart: curl and libcurl, distribution builds of Node.js and PHP, Python's python3-brotli and more. The status differs a lot between distributions:
| Distribution | Status | Fixed package |
|---|---|---|
| Debian 12 bookworm | Fixed (DLA-4855-1) | brotli 1.0.9-2+deb12u1 |
| Debian 13 trixie | Fixed (DSA-6582-1) | brotli 1.1.0-2+deb13u1 |
| Debian testing, unstable | Not fixed | - |
| Ubuntu (all releases) | Needs triage | - |
| Alpine 3.21, 3.22 | Fixed | brotli 1.1.1-r0 |
| Alpine 3.23, 3.24 | Fixed | brotli 1.2.1-r0 |
| RHEL 8, 9, 10 | Affected, Important | - |
| Amazon Linux 2023 | Pending fix | - |
# Debian / Ubuntu: fixed builds are 1.0.9-2+deb12u1 (bookworm), 1.1.0-2+deb13u1 (trixie)
dpkg -l libbrotli1
# Alpine: want 1.1.1-r0 (3.21, 3.22) or 1.2.1-r0 (3.23, 3.24)
apk info -v brotli-libs
# RHEL, Fedora, Amazon Linux
rpm -q brotli# Debian
sudo apt-get update && sudo apt-get install --only-upgrade libbrotli1
# Alpine (also in Dockerfiles)
apk update && apk upgrade brotli-libs
# Then restart services that load it, for example curl-based workers, PHP-FPM, Node.js
# from the distribution, and rebuild container imagesContainers keep whatever was installed when the image was built. Rebuild from an updated base image (docker build --pull) and redeploy; patching the host does nothing for them.
Check any machine or image
Do not rely on Dependabot, pip-audit or npm audit alone. They only know about packages that have an advisory: they flag Python's brotli and brotlicffi, but not runtimes such as Node.js or .NET, copies bundled inside other packages, or operating system libraries.
The fix added a new error name to the C decoder's string table, so a compiled file that contains the decoder's error names but not the new one is unpatched. The Python checker from the Python guide uses this and scans any folder you give it, whatever language the files belong to. It only needs Python 3.8 or later on the machine doing the scan:
# Any folder: system libraries, an app, a JDK, an extracted image
python3 brotli_check.py /usr/lib /opt/myapp
# Inside a container that has Python
docker run --rm -v "$PWD:/check" my-image python3 /check/brotli_check.py /usr/lib /appVULNERABLE /usr/lib/x86_64-linux-gnu/libbrotlidec.so.1.0.9 (Ubuntu 22.04)
VULNERABLE D:\Program Files\Git\mingw64\bin\libbrotlidec.dll (Git for Windows 2.44.0)
VULNERABLE mingit\ucrt64\bin\libbrotlidec.dll (Git for Windows 2.56.0.windows.3)
VULNERABLE brotli4j\native-linux-x86_64\...\libbrotli.so (Brotli4j 1.23.0)
VULNERABLE dotnet\shared\Microsoft.NETCore.App\3.1.32\clrcompression.dll (.NET Core 3.1)It cannot read builds that strip the error names, such as .NET's current Windows DLL; check those by version. For everything else, these are the quick checks:
node -v # 26.12.1, 24.22.1 or 22.23.4
bun --version # 1.4.4 or later
go version -m ./server | grep andybalholm/brotli # v1.2.7 or later
dotnet --list-runtimes # see the .NET section: no fixed runtime yet
php --ri brotli # extension version; check the bundled library
php -r 'var_dump(curl_version()["brotli_version"] ?? null);' # libcurl's Brotli
gem list brotli # Ruby gem version
mvn dependency:tree | grep -i brotli # or: gradle dependencies | grep -i brotli
envoy --version # 1.40.0, 1.39.3, 1.38.6, 1.37.8 or 1.36.12
curl -V # shows brotli/x.y.z (distribution backports keep the old number)What to fix first
- 1Anything that decodes Brotli from strangers. Public APIs that accept compressed request bodies (Express 5, aiohttp, ASP.NET Core with request decompression), Envoy with the decompressor filter, and services that fetch user-supplied URLs.
- 2Runtimes with a fix: Node.js, Bun, Python packages, the Go module, Envoy and Istio. Update, rebuild images, restart.
- 3The operating system package, on distributions that have one, then restart everything that loads it.
- 4Copies with no fix yet (.NET, Electron, Brotli4j, PHP and Ruby extensions, pyarrow, Pillow): keep untrusted Brotli data away from them and check again weekly.
Common questions
What are Node2Shell and HTTP2Shell?+
Which languages are affected by the Brotli vulnerability CVE-2026-111497?+
Is Go affected by CVE-2026-111497?+
Is .NET affected by the Brotli vulnerability?+
Does updating libbrotli fix Node.js or Python?+
Are nginx and Apache affected?+
Want your stack checked?
I update runtimes, dependencies and base images across Node.js, Python and PHP services, and verify the fix where the code runs. See Node.js, Python and Next.js bug fixing or PHP security updates.
Sources
Next steps
Keep reading
Need a hand with this?
Tell me what you are working on. Free scoping call, fixed quote within 48 hours.
Book a call