Skip to main content
株式会社オブライト
Software Development2026-10-2513 min read

Brotli Vulnerability CVE-2026-111497 (Node2Shell / HTTP2Shell)

How to Check Exposure and Patch Node.js, Python, Bun and Envoy

CVE-2026-111497 (HTTP2Shell): a crafted Brotli stream causes an out-of-bounds write. Fixed versions for Node.js, Python, Bun, check commands and mitigations.


CVE-2026-111497 is an out-of-bounds write in Google's Brotli decoder (versions 1.0.3 to 1.2.0) that a crafted compressed stream can trigger. It was first called "Node2Shell" in the Node.js disclosure and became "HTTP2Shell" once it turned out to affect HTTP libraries and applications broadly. Node.js, Python, Bun, Envoy, Istio, curl and anything else that decodes Brotli-compressed HTTP traffic is in scope. Check which Brotli version you run and which code paths feed it untrusted data, then update to a fixed release (libbrotli 1.2.1; Node.js 22.23.4, 24.22.1 or 26.12.1; and so on).

This article is based on Aikido Security's write-up and on coverage of the October 2026 Node.js security release, published between October 21 and 24, 2026. It covers how to tell whether you are exposed, what to do per runtime, and what to do while you cannot patch. Versions and dates are limited to what those sources confirm; where something is unclear we say "check the official advisory". Severity differs by source: Aikido's article rates it CVSS v4.0 9.2 (Critical), while Node.js rates it High.

The short version

- What: DecodeBlockTypeAndLength() in the Brotli decoder mishandles its return value, so a crafted stream causes an out-of-bounds write. The impact ranges from a process crash and memory disclosure to memory corruption and possible arbitrary code execution
- Who: anyone whose environment decodes Brotli data from an untrusted source. A request with Content-Encoding: br and a br response fetched from an external server are both entry points
- What to do: identify the runtime and package versions you use and update. If you cannot, turn Brotli decoding off. Dependency scanners may not flag the PyPI Brotli package, so check manually

Affected and fixed versions

The table lists the affected and fixed versions confirmed by the sources. OS vendors sometimes backport the fix without changing the version number, so an old-looking number can already be patched. Conversely, do not call something safe on the version number alone; read your distribution's security advisory.

ProductAffectedFixed
Google Brotli (libbrotli)1.0.3 to 1.2.0 (all platforms)1.2.1 (1.0.10 / 1.1.1 on the older lines)
PyPI Brotli1.2.0 and earlier1.2.1
PyPI brotlicffi1.2.0.x1.2.1.0
Node.jsUnpatched 22, 24 and 26 releases (demonstrated on 24.22.0)22.23.4 / 24.22.1 / 26.12.1. No fix for Node.js 20 and earlier
BunBefore 1.4.41.4.4
EnvoyUnpatched releases1.40.0 / 1.39.3 / 1.38.6 / 1.37.8 (1.36.12 also has it, but 1.36 is out of support)
IstioUnpatched releases1.30.6 / 1.31.2 (1.29.9 exists, but 1.29 is EOL)
curl / libcurlBuilds that depend on BrotliUpdate the Brotli side (1.2.1, patched older lines, or your distribution's package)
Apache / NginxNot affected by default, since neither decodes Brotli by defaultApache's stock mod_brotli does not decode either

Where the table says "before" or "unpatched", the sources do not give an exact lower bound, so confirm the precise range in each project's own advisory. The Python advisory is GHSA-9v8h-qqgm-4m35.

How the vulnerability works

Brotli is the compression behind Content-Encoding: br. It produces smaller output than gzip, so browsers, CDNs and API servers use it widely. The bug is on the decoding side. The Brotli decoder has a function, DecodeBlockTypeAndLength(), that returns a bool. A false return means both "the stream is invalid" and "more input is needed", so the caller cannot tell the two apart. On the fast path the return value was dropped altogether. In CWE terms this is CWE-252 (unchecked return value) leading to CWE-787 (out-of-bounds write).

An attacker builds a stream that forces an impossible block switch. The decoder does not notice the stream is invalid, keeps decoding with the stale block state, and writes past the end of the buffer. The result can be a crash (denial of service), memory disclosure, memory corruption and, under the right conditions, arbitrary code execution. How easy it is to exploit depends on memory layout, OS mitigations and the runtime's implementation, so the outcome is not the same everywhere.

The upstream fix is google/brotli commit 0ed4268. It adds the error code BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (value -17) so that an invalid block switch is reported as an error. In other words, this is not a Node.js or Python bug; every product that ships the Brotli code had it. That is why the nickname moved from Node2Shell to HTTP2Shell.

Attack paths: both inbound and outbound

The sources describe the attack as bidirectional. It is not only your server being attacked; your own program connecting to a malicious server is just as exposed, and that case is easy to overlook.

- Inbound: apps or middleware that decode a request body sent with Content-Encoding: br. The sources use Express 5 on Node.js as an example. The attacker only needs to POST a crafted Brotli body
- Outbound: your program fetches and decodes a Brotli response from a malicious server. Webhook verification, URL previews (link thumbnails), crawlers, third-party API integrations and any fetch-by-URL feature fall here
- Indirect: the application may not care about Brotli, yet the HTTP client library finds a Brotli package and enables it automatically. In Python, urllib3 (including via requests), httpx (the brotli extra), aiohttp and Scrapy do this

Inbound, an attacker's client sends a crafted Brotli body that your server or middleware decodes; outbound, a malicious server returns a crafted response that your app's HTTP client decodes. Both reach the vulnerable Brotli decoder (1.0.3-1.2.0).

In Python especially, merely having Brotli installed deep in the dependency tree makes the HTTP client send br in Accept-Encoding and decode the answer. The Brotli package sees about 82.8 million downloads a month, and it is often there as someone else's dependency rather than something you installed deliberately.

As of October 22, 2026, no exploitation in the wild had been reported. Researchers including those at Imperva, however, have demonstrated exploitation on an unmodified Node.js 24.22.0 as a research demo. With a public demo, assume working exploit code will spread quickly and move fast.

How to check whether you are exposed

Work through three questions: which runtime and version you run, whether a Brotli package is installed, and whether any path decodes untrusted Brotli data. Start with Python packages. For containers, inspect the image that actually runs.

# 1. Brotli packages and versions in the Python environment
pip show Brotli brotlicffi

# 2. Inspect a container image (replace the image name)
docker run --rm <image> pip show Brotli brotlicffi

# 3. What pulled Brotli in? (reverse dependency lookup)
pipdeptree -r -p Brotli

# 4. Is it in a lockfile or requirements file?
grep -i brotli poetry.lock uv.lock requirements*.txt

For Node.js, check the runtime version and the Brotli version it bundles. process.versions.brotli returns the bundled Brotli version. Treat the Node.js release number as the primary signal and the bundled Brotli number as a secondary hint.

# Node.js version (fixed: 22.23.4 / 24.22.1 / 26.12.1)
node -v

# Brotli version bundled with Node.js
node -p process.versions.brotli

Then review your design for paths that decode untrusted Brotli data.

- Does any public server accept and decode requests with Content-Encoding: br (Express 5 body parsers, API gateways, Envoy or Istio filters)?
- Does anything fetch external URLs (webhook delivery, URL previews, image fetching, crawlers, third-party APIs)?
- Does that HTTP client send br in Accept-Encoding? Check the library's defaults
- Do several container images, serverless functions or CI runners each carry their own copy of the same Brotli library?

Fixes by language and platform

Node.js

Node.js fixed this in its October 2026 security release, which addressed 10 vulnerabilities, 4 of them rated High. The fixed versions are 22.23.4, 24.22.1 and 26.12.1. No fix is provided for Node.js 20 and earlier, so if you are on those lines your only real option is to move to a supported line (22 or later). If you cannot move immediately, use the mitigations below (reject br requests, disable outbound br) in the meantime.

Also check that your Docker base image (a tag such as node:22) has not gone stale. The tag name can stay the same while an already-built image keeps the old Node.js. The fix only lands once you rebuild the image and redeploy.

Python

On PyPI, Brotli 1.2.0 and earlier and brotlicffi 1.2.0.x are affected; the fixed versions are 1.2.1 and 1.2.1.0. If you pin them in requirements or a lockfile, raise the pin, regenerate the lock and rebuild the container image. urllib3, httpx, aiohttp and Scrapy use the Brotli package automatically when it is present, so updating the package is the fix; you do not need to change those libraries.

# Update to the fixed versions (use pip / uv / poetry as appropriate)
pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0"

# Verify after updating
pip show Brotli brotlicffi

Note that many Python dependency-analysis tools do not flag this issue (see below). Brotli is distributed as a wheel containing compiled C code, so it carries its own copy independent of the system libbrotli. Updating the OS package does not update the Brotli inside a Python virtual environment.

Bun

Bun fixed this in 1.4.4. Update with bun upgrade, and raise the Bun version pinned in your CI and container images as well.

Envoy and Istio

Envoy is fixed in 1.40.0, 1.39.3, 1.38.6 and 1.37.8. The fix is also in 1.36.12, but the 1.36 line is out of support. Istio's fixed versions are 1.30.6 and 1.31.2; 1.29.9 exists too, but 1.29 is end of life. In a service mesh or API gateway, the proxy is exposed when its Brotli decompressor filter is enabled. If your configuration does not use that filter, exposure is limited, but confirm it from the configuration before concluding that.

curl and OS packages

curl and libcurl use a Brotli library as an external dependency, so what needs updating is the linked Brotli, not curl itself. Move to libbrotli 1.2.1, the patched older lines (1.0.10, 1.1.1) or your distribution's fixed package. On Debian-family, RHEL-family and Alpine systems the fix may arrive without a version change, so check each distribution's security tracker for how it handles CVE-2026-111497. Statically linked binaries and apps that bundle Brotli are not fixed by an OS update and must be rebuilt.

Mitigations while you cannot patch

If a fixed version is not available (Node.js 20 or earlier, verification still pending, waiting on a vendor), stop using Brotli decoding to close the entry point. These are not root-cause fixes, and they do not help against decoders bundled by other runtimes. The lasting fix is updating.

Python mitigations

- Uninstall Brotli and brotlicffi. HTTP clients fall back to gzip or deflate, but that is not guaranteed for every library, so test it
- Remove extras such as httpx[brotli] from your dependencies and regenerate the lockfile
- Restart the environment (or redeploy the container) so already-loaded modules are replaced
- Decoders bundled by other runtimes, such as Node.js, remain, so do not stop at Python

Inbound mitigations

At the proxy, load balancer or application middleware, reject requests whose Content-Encoding contains br. Compare case-insensitively and also match when br appears inside a list such as gzip, br. A naive exact-match check on the header value lets BR or gzip, br slip through. Ordinary API clients rarely send request bodies in Brotli, so rejecting them usually has little side effect.

Outbound mitigations

Remove br from Accept-Encoding. That alone is not enough: a malicious server can ignore Accept-Encoding and still answer with Content-Encoding: br. You also need to disable decoding of br responses in the client, or reject them. Start with features where you do not choose the other side, such as webhooks, URL previews and crawlers.

Scanners that do not flag this

As of October 23, 2026, pip-audit, Dependabot and Safety had not mapped this CVE to the PyPI Brotli and brotlicffi packages, so these tools raise no alert. The release notes of the fixed versions do not mention the CVE either. A green scanner therefore does not mean you are safe. Check versions directly with the commands above, and build manual verification into your routine until the vulnerability databases catch up.

This is not the first time such a gap has mattered. In the LiteLLM 1.107.0 tampering incident, people had to check their dependencies proactively. In the urgent PHP vulnerabilities CVE-2026-109618/109619, vendor information and scanner coverage also diverged for a while. There is a delay between a vulnerability being published and tools detecting it, and during that window you have to check yourself.

Timeline

DateEvent
February 2026OpenAI discovers the issue and reports it as unauthenticated remote code execution. The source is fixed, but it is not recognized as a security issue and the release comes later
October 21, 2026Node.js discloses the CVE (a release fixing 10 vulnerabilities, 4 of them High)
October 22The Python-side fix lands. No exploitation in the wild reported at this point
October 23Scope widens to other languages and HTTP applications. Patches ship for libbrotli, Python, C++, Node.js, Bun and Envoy
As of October 23pip-audit, Dependabot and Safety do not map the CVE to the PyPI Brotli packages and do not detect it
October 21 to 24Discussed on Hacker News (319 points). Researchers demonstrate exploitation on an unmodified Node.js 24.22.0 (research demo)

OpenAI's disclosure is published at patchtheplanet.org/brotli. For the detailed technical history and the latest exploitation status, check the official announcements and each vendor's advisory. The status in this article reflects what could be confirmed up to October 24, 2026.

Checklist for today

- List the versions of Node.js, Bun, Envoy or Istio, and the Python Brotli packages you use, and compare them with the table above
- Inspect the contents of container images (the image that actually runs, not the source)
- Find every path that decodes untrusted Brotli data (Content-Encoding: br requests, webhooks, URL previews, crawlers)
- Update to the fixed versions, rebuild images, redeploy and regenerate lockfiles
- For anything you cannot update, add rejection of br requests and disable outbound br
- Do not trust scanner results alone; keep the output of the check commands in your ticket or records

Frequently asked questions

What causes CVE-2026-111497?

In Google Brotli's decoder (1.0.3 to 1.2.0), DecodeBlockTypeAndLength() cannot tell an invalid stream from insufficient input through its return value, and the fast path discarded the return value. A crafted stream forces an impossible block switch, decoding continues with stale block state, and an out-of-bounds write results.

Are Node2Shell and HTTP2Shell different vulnerabilities?

No, they are nicknames for the same CVE-2026-111497. It was called Node2Shell in the Node.js disclosure, then HTTP2Shell once it became clear that HTTP libraries and applications in general were affected.

Am I affected if I run Nginx or Apache?

Apache and Nginx do not decode Brotli by default, and Apache's stock mod_brotli does not decode either, so they are normally out of scope. But a Node.js or Python application running on the same server that decodes Brotli is affected.

If pip-audit or Dependabot report nothing, am I safe?

Not necessarily. As of October 23, 2026, pip-audit, Dependabot and Safety do not map this CVE to the PyPI Brotli or brotlicffi packages, so they do not detect it. Check the version directly with pip show.

I am on Node.js 20. What should I do?

No fix is provided for Node.js 20 and earlier. Moving to a supported line, 22 or later, is the lasting answer. Until you can, close the entry points by rejecting requests whose Content-Encoding contains br and by disabling outbound br.

Is removing br from Accept-Encoding enough?

No. A malicious server can ignore Accept-Encoding and answer with br anyway, so you also need to disable decoding of br responses in the client or reject them.

Has this been exploited yet?

As of October 22, 2026, no exploitation in the wild had been reported. Researchers including those at Imperva have shown it working on an unmodified Node.js 24.22.0 as a research demo. Check the official announcements for the latest status.

Feel free to contact us

Contact Us