HTTP2Shell Vulnerability in Brotli Affects More Than Node.js and HTTP Clients

The Brotli decoder vulnerability affects not just Node.js, but Python libraries, HTTP clients, and other applications. Updating dependencies is not enough: applications must be rebuilt and redeployed.

· 4 min read · 5 comments

The Brotli decoder vulnerability first reported in connection with Node.js also affects other libraries and applications that use HTTP. As Aikido reported on October 24, 2026, the flaw, CVE-2026-111497, is triggered when specially crafted data is decompressed. Fixes are available, but updating a dependency in the repository is not enough: the vulnerable decoder may remain inside a built application or container.

Why Node2Shell Became HTTP2Shell

Brotli is used to compress data sent over HTTP, including HTML, CSS, and JavaScript. The problem lies in the decoder: a crafted stream can cause a write beyond allocated memory. Google Brotli versions 1.0.3 through 1.2.0 are affected on all platforms. The vulnerability has a CVSS 4.0 score of 9.2.

The bug occurs when switching between blocks in a compressed stream. Previously, one function result could indicate either an invalid format or insufficient input data. The calling code did not always distinguish between the two, and the fast path did not check the result. Decoding could continue with the previous block state; the fix stops it when a format error occurs.

An attacker without an account can submit such a stream and cause a process crash, data leakage, or potentially code execution. The risk runs in both directions: a server may decompress the body of an incoming request, while an HTTP client may decompress a response from another server. Checking only public routes is not enough.

OpenAI discovered the bug back in February. A fix went into the Brotli source code at the time, but it was not recognized as a security fix, and a release containing it has only now come out. Projects that bundle their own copy of the library need separate updated builds: a patch in the source code does not change a binary that is already running.

Where to Look for a Vulnerable Decoder

In Python, Brotli version 1.2.0 and earlier and the brotlicffi 1.2.0.x series are affected. Developers do not have to import them directly. If they are installed, urllib3 and requests, which uses urllib3, may automatically decode Brotli responses. httpx also supports Brotli with its Brotli extra, as does aiohttp when the package is installed. It can also enter a project through another dependency, such as Scrapy.

The fixed Python versions are Brotli 1.2.1 and brotlicffi 1.2.1.0. For Node.js, the listed versions are 22.23.4, 24.22.1, and 26.12.1 in their respective supported branches; for Bun, it is 1.4.4. Envoy fixes are included in 1.40.0, 1.39.3, 1.38.6, and 1.37.8, and Istio fixes in 1.29.9, 1.30.6, and 1.31.2. At the same time, Envoy 1.36 and Istio 1.29 are no longer supported, although fixes for them are also listed in the report. A system package may receive a vendor patch without its version number changing to match a Brotli release.

There is also a gap in the usual dependency checks. As of October 23, advisory databases had not yet linked this CVE to the Brotli and brotlicffi packages on PyPI. As a result, pip-audit, Dependabot, and Safety may not yet flag a vulnerable version. The absence of a warning does not mean the decoder is absent.

What Websites and Applications Should Do

Developers should check installed versions in every environment, including containers, and inspect the full dependency tree. A sensible place to start is with services that accept compressed request bodies or contact third-party servers—for example, to fetch a page for a URL preview. Website owners should find out whether their projects have such features and whether the components that process data for them have been updated.

After updating, regenerate lockfiles, rebuild applications and images, redeploy them, and check the components in running processes. Embedded and statically linked copies of Brotli need particular attention. If a service is managed by a vendor, you will need to confirm the status of the fix with them.

If updating is not yet possible, incoming requests with Content-Encoding: br can be rejected before decompression. For outgoing requests, simply removing br from Accept-Encoding is not enough: a server may send a Brotli response even without that header. You also need to disable decoding of those responses or reject them before processing the body. In Python, where Brotli is optional, you can temporarily remove both packages and any dependencies that would reinstall them. But some responses will then become unreadable, and decoders inside other components will remain.

My Take

I would not start by searching my own code for the word Brotli. A five-year-old “temporary” service could easily be decompressing responses through a dependency its author forgot about long ago. I would first look at installed packages and running images, then at the places where data arrives from other servers. A green check mark in a scanner report that does not yet link the CVE to PyPI packages offers reassurance too soon this time.

Sources

Where the news comes from. The text is a retelling in the author’s own words; the facts come from the source, the opinion is the author’s.

  1. Node2Shell evolves to HTTP2Shell, blast radius affects many HTTP libs and apps www.aikido.dev

About the author

Lev Ivanenko

experienced full-stack developer

I build internal dashboards and shops, and most often end up untangling old code that’s been “temporary” for five years already. In my free time, I check how many libraries I can remove without consequences.

All posts by the author

Leave a comment

Not published. We’ll send a link to confirm your comment.

No links or ads. Your first comment is published after review. By sending a comment you agree to the privacy notice.

  1. The rebuild-and-redeploy point seems easy to miss: how should teams verify that the decoder actually shipped in a container has been replaced, especially when it’s bundled or statically linked rather than visible as a direct dependency?

    1. well, check the final image, not just the lockfile: generate an SBOM and scan it, then verify the bundled or statically linked Brotli version against the fixed release using build metadata or binary inspection. if the scanner can’t identify it, that’s not proof it’s gone; rebuild from a clean base and keep the build provenance so you can trace what actually shipped.

    2. On our store launch, we’ve started treating the container image—not just the package file—as the thing to verify: rebuild from a clean base, scan the final image, then deploy that exact image and keep its digest with the release notes. That gives us a practical check even if Brotli is bundled or statically linked, without turning every dependency update into a manual hunt 🙂

      1. Timur, does the final-image scan identify the Brotli decoder version when it is statically linked, or do you also need a runtime check? I’d want that distinction in the release record before treating the image as verified.

        1. ну, a final-image scan can identify Brotli when it’s packaged as a recognizable component; static linking is where scanners may miss it or only flag the binary without proving the decoder version. I’d record the scan result plus build metadata or binary inspection against the fixed release, and use a runtime check only if it can reliably identify the loaded decoder—not as a substitute for that evidence.