From Node2Shell to HTTP2Shell (CVE-2026-111497): How the Brotli RCE Vulnerability Impacts Python, Java & the Polyglot Web Ecosystem
Why CVE-2026-111497 is much bigger than Node.js: how the CVSS 9.2 Out-of-Bounds Write in Google C libbrotli (c/dec/decode.c) crosses C-extension and JNI boundaries into Python (urllib3, requests, aiohttp, httpx), Java (brotli4j, Netty, Spring Cloud Gateway), Rust, and C++ proxies.
c/dec/decode.c, versions 1.0.3β1.2.0), any runtime or networking library that binds to native libbrotliβincluding Python (brotli, brotlicffi, urllib3, aiohttp, httpx), Java (brotli4j, Netty, Reactor Netty, Vert.x), Node.js, Bun, Rust (brotli-sys), PHP, and C++ proxiesβis exposed when unpacking untrusted Brotli streams.In our previous advisory on Node2Shell (CVE-2026-111497), we dissected how an Out-of-Bounds Write (CWE-787) inside Google's Brotli compression library enabled pre-authentication Remote Code Execution (RCE) against Node.js servers running Express 5, body-parser, or native fetch().
However, in the days following the October 21 disclosure, a crucial realization swept across security engineering teams worldwide: framing CVE-2026-111497 as a "Node.js bug" misses 80% of the blast radius.
The vulnerable code in c/dec/decode.c is not specific to V8 or JavaScript. It is the canonical C reference implementation of RFC 7932 (Brotli) maintained by Google and embeddedβvia Python C-extensions, Java Native Interface (JNI) JARs, Rust FFI crates, Go cgo wrappers, and shared OS libraries (libbrotlidec.so)βinto virtually every modern language ecosystem on the internet. As advisories expanded across Python, Java, C++, Bun, and edge proxies, the vulnerability earned a far more accurate name: HTTP2Shell.
In this continuation deep dive, we trace how a single C buffer overflow crosses language boundaries into "memory-safe" runtimes like Python and Java, map the exact dependency chains that silently enable Brotli decoding behind your back, and provide a polyglot audit, mitigation, and patching playbook.
1. Why "Memory-Safe" Languages Like Python and Java Are Vulnerable
A common reaction from backend teams running Python (Django, FastAPI, Celery) or Java (Spring Boot, Netty, Quarkus) is: "We run in a managed virtual machine with bounds-checked arrays. How can a C buffer overflow hit our service?"
The answer is native performance bindings. Decompressing high-bandwidth HTTP streams in pure interpreted Python or pure JVM bytecode is computationally expensive. To achieve wire-speed decompression, the most popular HTTP libraries in both ecosystems wrap Google's C implementation (libbrotlidec) directly inside the host OS process:
Malicious HTTP Stream (Content-Encoding: br)
β
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
βΌ βΌ βΌ
βββββββββββββββββββββ βββββββββββββββββββββ βββββββββββββββββββββ
β PYTHON STACK β β JAVA / JVM STACK β β NODE / BUN / C++ β
βββββββββββββββββββββ€ βββββββββββββββββββββ€ βββββββββββββββββββββ€
β urllib3 / requestsβ β Netty / WebFlux β β Express 5 / Undiciβ
β aiohttp / httpx β β Vert.x / Gateway β β Bun / Envoy / C++ β
β β β β β β β β β
β βΌ β β βΌ β β βΌ β
β brotli (CPython) β β brotli4j (JNI) β β node:zlib (C++) β
β or brotlicffi β β libbrotlidec.so β β bundled brotli β
βββββββββββ¬ββββββββββ βββββββββββ¬ββββββββββ βββββββββββ¬ββββββββββ
β β β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
βΌ
Google C Reference Decoder: c/dec/decode.c
(Versions 1.0.3 through 1.2.0)
β
π₯ Out-of-Bounds Heap Write (CWE-787) in Process MemoryOnce execution crosses from Python (via the CPython C API / CFFI) or from Java (via JNI) into c/dec/decode.c, all managed runtime guardrails disappear. The C decoder operates directly on the native process heap alongside the Python interpreter's PyObject arenas or the JVM's native off-heap memory and thread stacks.
2. The Python Blast Radius: brotli, brotlicffi, urllib3, and aiohttp
In the Python ecosystem, HTTP2Shell is particularly insidious because
How Silent Auto-Activation Works in Python HTTP Clients
Python's standard library includes zlib (for gzip and deflate), but does not include a built-in Brotli module. However, look at what happens inside urllib3 (which powers requests), httpx, and aiohttp at import time:
- When
urllib3oraiohttpinitializes, it executes an optional import check forbrotli(Google's official C-extension wheel on PyPI) orbrotlicffi(the CFFI wrapper aroundlibbrotli). - Even if your code never explicitly installed
brotli, dozens of transitive dependenciesβsuch as web scrapers, cloud SDKs, Playwright/Selenium helpers, or API clientsβdeclarebrotliorurllib3[brotli]in their dependency tree. - As soon as
_brotli.cpython-*.sois present insite-packages,requests,urllib3,httpx, andaiohttpautomatically appendbrto the outboundAccept-Encodingheader on every HTTP call and automatically pipe anyContent-Encoding: brresponse straight into the vulnerable C decoder!
requests.get(), urllib3, httpx.AsyncClient(), or aiohttp.ClientSession()βas well as FastAPI/Starlette/Django apps using brotli-asgi or request-decompression middlewareβcan be compromised by a single malformed Brotli stream if brotli < 1.2.1 is installed.3. The Java & JVM Blast Radius: brotli4j, Netty, and Spring Cloud Gateway
In the Java ecosystem, there are two completely different ways applications decode Brotli streams, and understanding which one sits in your Maven or Gradle tree determines whether you face a safe Java exception or a critical native heap overflow:
| Java Library / Artifact | Implementation Type | Exposure to CVE-2026-111497 (CWE-787) |
|---|---|---|
com.aayushatharva.brotli4j:brotli4j(Used by Netty netty-codec-http, Reactor Netty, Spring WebFlux / Cloud Gateway, Vert.x) |
JNI Native C Wrapper (Bundles precompiled libbrotlidec.so / .dll / .dylib inside platform JARs) |
VULNERABLE TO NATIVE HEAP CORRUPTION & RCE if bundling Google Brotli 1.0.3β1.2.0. Bypasses JVM memory safety via JNI. |
Custom JNI / JNA / Panama Foreign Function Bindings to OS libbrotlidec.so.1 |
Native OS Shared Library | VULNERABLE unless system libbrotli package is patched to 1.0.10 / 1.1.1 / 1.2.1+. |
org.brotli:dec(Used by default in okhttp3:okhttp-brotli & Apache HttpClient pure-Java paths) |
100% Pure Java Bytecode | No Native RCE: JVM bounds checking throws an ArrayIndexOutOfBoundsException / BrotliRuntimeException instead of corrupting native heap memory. |
How Netty Automatically Activates JNI Brotli (brotli4j)
In high-performance Java stacks built on Netty (io.netty:netty-codec-http), (the engine underneath and ), or , adding HttpContentDecompressor to the pipeline automatically checks whether com.aayushatharva.brotli4j.Brotli is present on the classpath (Brotli.isAvailable()).
If brotli4j is present, Netty unpacks the bundled native shared library (libbrotlidec.so from brotli4j's native-linux-x86_64 or native-linux-aarch64 JAR) into /tmp and loads it into the JVM via System.load(). From that moment on, every incoming Content-Encoding: br HTTP request or response is fed directly into the native C BrotliDecoderDecompressStream() function inside the JVM process!
4. Other Languages, Native Bindings & Infrastructure Matrix
Beyond Node.js, Python, and Java, any language or infrastructure binary that statically or dynamically links Google's C libbrotli (1.0.3 through 1.2.0) is affected:
- Rust (
brotli-sys&async-compressionwith C backend): While the pure-Rustbrotlicrate is memory-safe, Rust crates that link against Clibbrotliviabrotli-syscross an FFIunsafeboundary intoc/dec/decode.c. - Go (
google/brotli/go/cbrotli): Go services that compile withcgoagainstcbrotli(rather than the pure-Goandybalholm/brotlipackage) execute the vulnerable C decoder. - PHP (
ext-brotli) & Ruby (brotliC gem): Native C extensions that wraplibbrotlidecfor PHP and Ruby web apps. - Bun & Deno: Alternative JavaScript/TypeScript runtimes that bundle native Brotli in their HTTP client and server pipelines.
- C/C++ Proxies, Gateways & CLI Tools (
Envoy,Apache httpd mod_brotli,Nginx ngx_brotli,curl): Any reverse proxy, service mesh sidecar, or CLI tool that decompresses untrusted Brotli streams using unpatchedlibbrotlidec.
5. Quick Recap: The C-Level Bug in c/dec/decode.c
As detailed in our Node2Shell technical analysis, the root cause inside c/dec/decode.c stems from how the Brotli state machine (BrotliDecoderStateStruct) handles across literal, command, and distance block categories.
When a crafted Brotli bitstream encodes an invalid block-type switch sequence, unpatched versions (1.0.3β1.2.0) fail to abort early, calculating an out-of-bounds index and writing past the allocated buffer into adjacent heap memory (including custom allocator callbacks alloc_func/free_func, ringbuffer pointers, or adjacent CPython/JNI/V8 heap structures). Upstream commit 0ed4268 fixes the vulnerability by enforcing strict block-switch validation and immediately returning BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH = -17.
6. Polyglot Audit, Patching & Mitigation Playbook
Step 1: Audit Every Layer of Your Stack
Run these four commands across your servers, CI runners, and container images to find every vulnerable copy of Brotliβboth at the OS level and hidden inside language package managers:
# 1. OS Shared Library Check (Debian/Ubuntu, RHEL/Alma, Alpine)
dpkg -l | grep -i brotli || rpm -qa | grep -i brotli || apk info -vv | grep -i brotli
# 2. Python Virtualenv / Container Check
python3 -c "import brotli; print('Python brotli:', brotli.__version__)" 2>/dev/null
pip list | grep -iE "brotli|brotlicffi|urllib3|aiohttp|httpx"
# 3. Java Maven / Gradle & Fat-JAR Check (Search for JNI brotli4j)
mvn dependency:tree | grep -iE "brotli4j|netty-codec-http"
# Or scan deployed JARs/WARs on disk for bundled native libbrotlidec binaries:
find / -name "*brotli4j*.jar" 2>/dev/null
# 4. Node.js Bundled Version Check
node -p "({ node: process.version, brotli: process.versions.brotli })"Step 2: Apply Upstream Patches Across Runtimes
- Google
libbrotli(C/C++ & OS Packages): Upgrade to1.2.1+(or backported maintenance releases1.1.1/1.0.10containing commit0ed4268). - Python: Upgrade
Brotliandbrotlicffiin your requirements (pip install --upgrade "Brotli>=1.2.1" brotlicffi). If your Python microservice does not need Brotli decompression, uninstall the C-extension (pip uninstall brotli brotlicffi) sourllib3/requests/aiohttpautomatically fall back togzip. - Java (
brotli4j/ Netty): Upgradecom.aayushatharva.brotli4j:brotli4jandnativesto the latest release built against Brotli1.2.1+. If a patched native JAR is not yet deployed in your build, excludebrotli4jfrom your Maven/Gradle dependencies so Netty disables JNI Brotli decoding gracefully. - Node.js: Upgrade to
v26.12.1+,v24.22.1+, orv22.23.4+.
Step 3: Code-Level Workarounds for Python & Java
If you cannot rebuild and redeploy native dependencies immediately, disable automatic Brotli negotiation in code:
1. Python (requests / urllib3 / httpx / aiohttp):
# Explicitly restrict Accept-Encoding to gzip/deflate on outbound HTTP clients
import requests
session = requests.Session()
session.headers.update({"Accept-Encoding": "gzip, deflate"})
resp = session.get("https://untrusted-webhook.example.com/data")2. Java (Maven Dependency Exclusion for JNI brotli4j):
<!-- Temporarily exclude JNI brotli4j so Netty HttpContentDecompressor skips native Brotli -->
<exclusions>
<exclusion>
<groupId>com.aayushatharva.brotli4j</groupId>
<artifactId>brotli4j</artifactId>
</exclusion>
</exclusions>Step 4: Perimeter WAF & Reverse Proxy Protection (Nginx / ModSecurity / Coraza / Centinela)
Regardless of whether your backend is written in Python, Java, or Node.js, you can neutralize inbound HTTP2Shell exploit attempts at the network edge by rejecting unsolicited Content-Encoding: br client requests:
# Nginx Reverse Proxy: Block inbound client requests compressed with Brotli
if ($http_content_encoding ~* "\bbr\b") {
return 415;
}# ModSecurity / Coraza / Centinela SecRule (Protects Node.js, Python, Java, and C++ backends)
SecRule REQUEST_HEADERS:Content-Encoding "(?i)\bbr\b" \
"id:1011497,phase:1,deny,status:415,log,t:none,msg:'HTTP2Shell / Node2Shell (CVE-2026-111497) Inbound Brotli Request Blocked'"7. Threat Hunting Across Polyglot Workloads
When an attacker probes a Python, Java, or Node.js service with malformed Brotli payloads, failed heap layout attempts reliably trigger native segmentation faults (SIGSEGV) inside libbrotlidec:
Python Workloads (Gunicorn / Uvicorn / Celery): Search supervisor and systemd logs for worker processes dying with signal 11 (SIGSEGV) or signal 6 (SIGABRT):
journalctl -u gunicorn -u uvicorn -u celery --since "7 days ago" | grep -E "signal 11|SIGSEGV|SIGABRT|core dumped"Java / JVM Workloads: Search your container filesystems or crash directories for HotSpot native crash logs (hs_err_pid*.log) where the crashing frame (C [libbrotlidec.so...] or BrotliDecoderDecompressStream) appears in the native stack trace:
grep -rnE "libbrotlidec|BrotliDecoder" /var/log/ /tmp/ /app/ 2>/dev/nullSummary
The evolution from Node2Shell to HTTP2Shell (CVE-2026-111497) carries an essential lesson for modern application security: memory safety is only as strong as the lowest-level C library in your call graph. Whenever a foundational C compression or parsing library like libbrotli suffers a heap overflow, it cuts horizontally across Node.js, Python, Java, Rust FFI, and C++ gateways alike.
Audit your virtual environments, JARs, and container base images for libbrotli 1.0.3β1.2.0, upgrade to Brotli 1.2.1+ (and Node.js 26.12.1 / 24.22.1 / 22.23.4), strip unsolicited Content-Encoding: br requests at your WAF, and restrict Accept-Encoding on outbound HTTP clients fetching untrusted URLs.