Contents
ToggleExecutive summary
HTTP2Shell (CVE-2026-111497) has outgrown its first two names. Node2Shell described a Node.js runtime fix on October 21, 2026. Python2Shell added PyPI Brotli and brotlicffi two days later. Four days after disclosure, the open exposure no longer sits in either. It sits in software that carries a private copy of Google’s Brotli C decoder: the .NET runtime, DuckDB, ClickHouse, Apache Arrow bundled builds, v8-jsi, 7-Zip-zstd, p7zip, EDK II firmware tooling and woff2 font builds, all still unpatched as of October 25 (Broccoli2Shell).
None of those copies is fixed by a distribution update, and several decode input that never crosses HTTP: Parquet files, fonts, archives and firmware images. That moves the attack surface from “who can send my server a request” to “who can hand my pipeline a file”. Meanwhile the first wave of fixes landed fast. The Ruby gem, PHP extension, UEFITool and MuPDF shipped patched releases, and Cloudflare, Azure, Imperva and Vercel pushed WAF rules for inbound Brotli bodies.
Exploitation remains contested. Imperva achieved RCE on stock Node.js 24.22.0. The public PoC reaches code execution only on a weakened build, and Red Hat rates the flaw Important with no reliable public RCE. No in-the-wild attacks have been reported, and CISA KEV did not list it as of October 24.
Key takeaways
- The remaining exposure is vendored copies, not runtimes. If a product bundles Brotli, only that product’s own release fixes it.
- File parsing is now the second attack path. Brotli-compressed Parquet, WOFF2 fonts, archives and firmware sections reach the decoder without any HTTP traffic.
- .NET is the largest open population: BrotliDecoder, BrotliStream, HttpClient automatic decompression and ASP.NET Core request decompression all run bundled Brotli 1.1.0.
- Scanners fail in both directions: they miss vendored copies, and they flag distro packages with backported fixes as still vulnerable.
- WAF rules cover inbound HTTP only. Outbound fetches and file ingestion need the code fix.
TL;DR for engineering teams
What it is: The expansion phase of CVE-2026-111497, an out-of-bounds write in the Google Brotli C decoder 1.0.3 to 1.2.0. GitHub 9.2 Critical (CVSS 4.0), Node.js High, Red Hat Important.
Where it bites now: .NET runtime (bundled 1.1.0), DuckDB, ClickHouse, Apache Arrow bundled builds (1.0.9), v8-jsi (1.2.0), 7-Zip-zstd (1.2.0), p7zip (1.0.9), EDK II (1.2.0), woff2 Makefile builds (1.0.3).
Why it matters: Vendored copies ignore OS patching and often decode files, not HTTP bodies, so WAF rules and distro updates miss them.
Patch status: Upstream 1.0.10, 1.1.1, 1.2.1 available since October 21. Fixed since disclosure: Ruby gem 0.9.1, PHP extension 0.21.1, UEFITool A76, MuPDF 1.28.0. Main-branch only: NanaZip, GraalJS, YDB, libjxl.
Immediate action: Inventory vendored Brotli by SBOM and source search, isolate untrusted file ingestion into the unpatched data engines, apply vendor fixes as they ship, and keep the runtime upgrades from the first analysis in place.
What changed since the first analysis
Our first analysis covered the root cause, the Node.js and Python exploit paths and the runtime fixes. This follow-up tracks what moved between October 23 and 25.
| Area | October 23 view | October 25 view |
|---|---|---|
| Ruby brotli gem | Unpatched | Fixed in 0.9.1; refuses to build against unpatched system libbrotli |
| PHP brotli extension | Unpatched | Fixed in 0.21.1, bundles 1.2.1 |
| MuPDF | Unpatched | 1.28.x fixed (fork picked up the fix in April 2026); 1.27.x still affected |
| UEFITool | Unpatched | Fixed in A76 |
| v8-jsi, woff2, GraalJS | Not tracked | v8-jsi and woff2 Makefile builds open; GraalJS fixed on master, unreleased |
| Ubuntu | Needs evaluation | 24.04 fixed in libbrotli1 1.1.0-2ubuntu0.1 |
| Public PoC | Reported | RCE only on a weakened build; crash plus limited memory read on standard builds |
| Severity | GitHub Critical, Node.js High | Red Hat adds Important, no reliable public RCE |
| WAF | Imperva rule | Cloudflare emergency release, Azure guidance, Vercel rules |
| Bun range | <= 1.4.3 | 1.0.21 through 1.4.3 |
Sources: Broccoli2Shell, Red Hat, Cloudflare, Bun.
Expansion map by surface
Brotli has no single distribution channel, which is why one fix became dozens of separate releases. The decoder reaches software four ways, and each patches on its own schedule.

Brotli decoder distribution channels · patch status as of October 25, 2026
The runtimes and bindings that made the headlines are largely fixed. What remains open is concentrated in two places: the .NET runtime, and tools that parse Brotli-compressed files.
Runtimes
Node.js and Bun vendor the decoder into their binaries, so only a runtime upgrade fixes them. v8-jsi bundles Node.js and inherits the same paths; it still ships Brotli 1.2.0. GraalJS (graal-nodejs) merged the fix on October 22, but GraalVM ships security updates quarterly and the latest release, 25.4.4.1.1, predates it. The .NET runtime bundles Brotli 1.1.0, and every managed Brotli API sits on top of it (Broccoli2Shell).
Language bindings
PyPI Brotli and brotlicffi were fixed on October 22 and 23. The Ruby gem 0.9.1 and PHP extension 0.21.1 both shipped on October 24 with Brotli 1.2.1; the Ruby gem also refuses to build against an unpatched system libbrotli. Python bindings installed from a distribution (for example python3-brotli) are fixed through that distribution’s security updates, not PyPI.
Proxies, CDNs and HTTP clients
Envoy and Istio are fixed for deployments with the Brotli decompressor configured. Apache’s standard mod_brotli and Nginx do not decode Brotli by default. libcurl inherits whatever system libbrotli it links, so it is fixed once the distribution package is patched and the process restarted. Chromium, Firefox and ngx_brotli were fixed before the tracker started.
File parsers and tools
This is the new surface. DuckDB, ClickHouse and Apache Arrow bundled builds decode Brotli-compressed Parquet. 7-Zip-zstd and p7zip open .br files and archives. EDK II decompresses firmware sections. woff2 Makefile builds decode WOFF2 fonts. The next section covers why these matter more than they look.
Bundled copy tracker
The tracker below comes from broccoli2shell.com, which searched GitHub for vendored copies, read their headers and treats a copy as patched only if it includes the new BLOCK_SWITCH error code. It was last rechecked on October 25, 2026 (Broccoli2Shell).
Still unpatched
| Project | Bundled copy | Untrusted input it decodes | Tracking |
|---|---|---|---|
| .NET runtime | 1.1.0 | BrotliDecoder, BrotliStream, HttpClient automatic decompression, ASP.NET Core request decompression middleware | Not stated |
| DuckDB | Unpatched | Brotli-compressed Parquet | PR #27791 |
| ClickHouse | Own fork | HTTP bodies, files, Parquet | PR #127419 |
| Apache Arrow (bundled build) | 1.0.9 | Brotli-compressed Parquet | Issue #55166 |
| v8-jsi | 1.2.0 | Same paths as Node.js | Not stated |
| 7-Zip-zstd | 1.2.0 | Archives and .br files a user opens | Issue #579 |
| p7zip | 1.0.9 | Archives and .br files a user opens | Not stated |
| EDK II | 1.2.0 | Compressed firmware sections | Issue #13378 |
| woff2 (Makefile build) | 1.0.3 | WOFF2 fonts passed to woff2_decompress; the CMake build links the system library | Issue #207 |
Fixed since disclosure
| Project | Fixed in | Released? | Details |
|---|---|---|---|
| Ruby brotli gem | 0.9.1 | Yes, October 24 | Bundles 1.2.1; refuses to build against unpatched system libbrotli |
| PHP brotli extension | 0.21.1 | Yes, October 24 | Bundles 1.2.1 |
| UEFITool | A76 | Yes, October 24 | Bundles 1.2.1 |
| MuPDF | 1.28.0 | Yes | Fork picked up the upstream fix in April 2026; 1.27.x still affected |
| NanaZip | Main (3da1c6a) | No | Latest release 7.0.1873.0 predates the fix |
| GraalJS (graal-nodejs) | Master | No | Merged October 22; quarterly security releases; 25.4.4.1.1 predates it |
| YDB | Main (343f6be) | No | Patched October 23 |
| libjxl | Main (PR #5062) | No | Latest release v0.12.0 predates the fix |
Fixed before the tracker began
Node.js, Bun, Chromium, Firefox, brotlicffi, ngx_brotli, Brotli4j, SumatraPDF, Ghostscript, CatBoost, YTsaurus, and the Gentoo, Solus and VMware Photon packages.
The tracker is not complete. GitHub code search skips submodules and some very large repositories, and copies vendored at build time or held in private repositories never appear. A listed project may not reach the decoder in every configuration. Maintainers fix it by moving to 1.0.10, 1.1.1 or 1.2.1, or cherry-picking commit 0ed4268; the upstream fix also includes commits 50a9b70, b418fa8 and da98063.
This list changed every day this week, and it will keep changing as vendors ship fixes. Phoenix Blue, Phoenix Security’s free threat intelligence feed, follows issues like this one as they move: from one runtime to every product that vendors the same code, and from “unpatched” to “fixed” for each copy. Sign up free at phxintel.security.
Protect yourself with the latest threat intelligence, get free access to Phoenix Blue Today
Beyond HTTP: file formats as attack surface
The first analysis focused on two triggers: a Brotli request body and a Brotli response. The open copies add a third: a file. Brotli is an internal compression codec in several formats, and anything that parses those formats from an untrusted source runs the decoder without any Content-Encoding header involved.

| Format | Where Brotli appears | Open decoders | Realistic delivery |
|---|---|---|---|
| Parquet | Column chunk compression codec | DuckDB, ClickHouse, Apache Arrow bundled builds | Customer uploads, data lake ingestion, read_parquet() on a URL or bucket path, shared datasets |
| WOFF2 fonts | Font table compression | woff2 Makefile builds | Font processing, PDF and document rendering pipelines, web font tooling |
| Archives and .br files | Archive codec, standalone files | 7-Zip-zstd, p7zip | A user opens an attachment or download |
| Firmware images | Compressed firmware sections | EDK II | Firmware build and inspection tooling, update packages |
| PDF, JPEG XL | Embedded compression | MuPDF 1.27.x, libjxl releases before the fix | Document and image previews, thumbnailers |
This changes who can reach the bug. An HTTP exploit needs a reachable endpoint or a way to make your service fetch a URL. A file exploit needs only a place where your systems accept a file: a data upload, a shared bucket, an email attachment, a CI artifact. WAF rules see none of these.
Data platforms deserve particular attention. DuckDB and ClickHouse frequently run with broad credentials to object storage and warehouses, and they are often fed by untrusted parties by design, through partner feeds, customer exports and public datasets.
Exploitation status and conflicting assessments
Three credible sources disagree on how dangerous this is in practice, and the disagreement is about reliability, not possibility.
| Source | Assessment | Basis |
|---|---|---|
| Imperva Red Team (Ron Masas) | RCE achieved | Attacker code ran on the official, unmodified Node.js 24.22.0 Linux arm64 image, delivered as a fetched response and as an Express body. Several attempts needed; exploit unreleased (Imperva) |
| Public PoC, GitHub, October 22 | RCE on weakened builds only | Crash and limited memory read on standard builds (ikow/brotli2crash) |
| Red Hat | Important | No public, reliable RCE proof of concept (Red Hat) |
| GitHub Advisory | 9.2 Critical (CVSS 4.0) | Network, no privileges, no interaction (GHSA) |
| Python (OpenSourceMalware) | Behavior confirmed, no exploit attempted | 1.2.0 accepted a ~23 MB crafted stream under ASan; 1.2.1 rejected it with -17 (OpenSourceMalware) |
No attacks in the wild had been reported as of October 22, and CISA’s KEV catalog did not list the CVE as of October 24 (Atlasum). A public crash PoC lowers the bar for denial of service immediately, so availability impact on unpatched services is the near-term certainty even if reliable RCE stays out of reach.
For prioritization, treat code execution as proven and reliability as unsettled. That puts internet-facing and URL-fetching services first, where a single successful attempt is enough, and keeps file-ingestion paths close behind.
WAF coverage
| Provider | Coverage | Scope |
|---|---|---|
| Cloudflare | Emergency Managed Ruleset release, October 25 (Cloudflare) | Inbound |
| Microsoft Azure | Azure WAF guidance (Microsoft) | Inbound |
| Imperva | WAF rule (Imperva) | Inbound |
| Vercel | Rules deployed on disclosure day (Broccoli2Shell) | Inbound |
Every one of these protects request bodies only. None sees a response your service fetches or a file your pipeline reads.
Why scanners get this wrong in both directions

False negatives: vendored copies
A vendored decoder does not appear under the libbrotli1 or brotli package name. A .NET runtime, a DuckDB binary or a pyarrow wheel carrying its own Brotli shows up, if at all, as the parent product’s version. Container and SCA scanners match the CVE only when an advisory maps it to that product, and most of the open list has no such mapping yet. The Python half showed the same failure at disclosure: the CVE named only the upstream repository, so pip-audit, Dependabot and Safety did not reliably flag PyPI versions until GitHub added PYSEC-2026-4322 and PYSEC-2026-4323 (Broccoli2Shell).
False positives: distribution backports
The opposite failure hits patched hosts. Vendors may backport the fix without changing the upstream version number, so a version-only scanner keeps reporting a patched libbrotli1 as vulnerable. Ubuntu 24.04’s fix, for example, ships as 1.1.0-2ubuntu0.1, a 1.1.0 package that is not vulnerable (Ubuntu). One developer scan of a patched machine still reported the vulnerable version (dev.to). Match on the distribution’s fixed package version, not on the upstream Brotli version string.
The practical consequence
A dashboard can be wrong in both directions at once: red on hosts that are fixed, green on the .NET service and the Parquet ingestion job that are not. Closing this CVE needs a check against what is actually loaded or bundled, not just what the package manager reports.
Detection: finding vendored and bundled copies
Search source trees for vendored decoders
This mirrors the tracker’s method: find every copy of the decoder header, then flag the ones that lack the new error code.
# every vendored Brotli decoder header in the tree (submodules included)
git submodule update --init --recursive
find . -path '*brotli*' -name decode.h -print0 \
| xargs -0 grep -L 'BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH'
# any path printed = a vendored copy without the fix
Run it across application repos, forks of third-party tools, firmware trees and build images. It will not find copies inside prebuilt binaries or wheels; those need SBOM data or the vendor’s statement.
.NET
dotnet --list-runtimes
grep -rnE 'BrotliDecoder|BrotliStream|DecompressionMethods\.(Brotli|All)|AddRequestDecompression|UseRequestDecompression' --include=*.cs .
Prioritize services where HttpClient automatic decompression includes Brotli and calls external URLs, and ASP.NET Core apps with request decompression middleware enabled on internet-facing endpoints.
Data engines
pip show duckdb pyarrow clickhouse-connect 2>/dev/null
duckdb -c "SELECT version();"
clickhouse-client -q "SELECT version()"
python -c "import pyarrow as pa; print(pa.__version__, pa.Codec.is_available('brotli'))"
Then map which jobs read Parquet from sources outside your control: partner feeds, customer uploads, public buckets.
Archives, fonts and firmware
dpkg -l | grep -Ei 'p7zip|7zip'; rpm -qa | grep -Ei 'p7zip|7zip'
grep -rn 'woff2_decompress' --include=*.c --include=*.cc .
For EDK II, check the Brotli submodule in your firmware tree with the header search above.
Runtime confirmation
# processes that loaded the system decoder right now
sudo grep -l brotlidec /proc/*/maps 2>/dev/null
This only covers dynamically linked copies. Statically linked and vendored decoders never appear in /proc/*/maps under that name.
Commands for runtimes and Python are in the first analysis.
Remediation by surface
| Surface | Fix now | Until a fix ships |
|---|---|---|
| Node.js, Bun | 22.23.4 / 24.22.1 / 26.12.1; Bun 1.4.4; rebuild images and compiled executables; restart | No Node.js workaround exists; upgrade is the only complete fix |
| Python | Brotli 1.2.1, brotlicffi 1.2.1.0; regenerate lockfiles | Uninstall both and drop httpx[brotli]; fallback to gzip/deflate is not guaranteed if a server sends Brotli anyway |
| Ruby, PHP | Ruby gem 0.9.1, PHP extension 0.21.1 | n/a, fixed |
| Envoy, Istio | Envoy 1.40.0 / 1.39.3 / 1.38.6 / 1.37.8; Istio 1.29.9 / 1.30.6 / 1.31.2 | Disable the Brotli decompressor only if the app behind it does not decode Brotli itself |
| System libbrotli, curl | Distribution security update, then restart linked processes | Reject br responses before decoding |
| .NET | Watch for a runtime servicing release | Remove Brotli from HttpClient automatic decompression on calls to untrusted hosts; disable request decompression middleware on public endpoints |
| DuckDB, ClickHouse, Arrow | Vendor releases with the fix (PR #27791, PR #127419, issue #55166) | Stop reading Brotli-compressed Parquet from untrusted sources, or reject files whose column codec is Brotli before parsing; run ingestion with least-privilege credentials |
| 7-Zip-zstd, p7zip, NanaZip | NanaZip main; others pending | Do not open untrusted archives or .br files with these tools |
| EDK II, woff2 | Pending (issues #13378, #207) | woff2: build with CMake against a patched system library; EDK II: cherry-pick 0ed4268 into the firmware tree |
| MuPDF, libjxl | MuPDF 1.28.x; libjxl from main | Upgrade from MuPDF 1.27.x; avoid previewing untrusted JPEG XL with released libjxl |
For outbound HTTP clients generally: removing br from Accept-Encoding is not enough on its own, because a malicious server can send Brotli regardless and urllib3 chooses its decoder from the response header. Reject responses whose Content-Encoding includes br before automatic body processing starts.
After every fix: update lockfiles and pins, rebuild images, redeploy, restart, and verify the running workload uses the patched component.
Phoenix Security recommendations
The expansion phase is where CVE-anchored tooling stops working. The open copies carry no CVE mapping to their parent product, and the patched distro packages still trip version matches. Phoenix ASPM handles that by working from components and assets rather than from CVE IDs.
Contextual deduplication folds the runtime, SCA, container and vendored findings for CVE-2026-111497 into one item per asset, so a .NET service flagged by three scanners is one owned ticket. Reachability analysis separates a Parquet reader that only processes internal exports from one that ingests partner uploads, and a .NET service that calls only internal APIs from one whose HttpClient decompresses third-party responses. Attack surface management lifts the internet-facing and file-accepting services to the top. Ownership attribution routes the data-engine findings to the data platform team, which in most organizations is not the team patching Node.js.
Campaign configuration additions
Extend the HTTP2SHELL_BROTLI_2026 campaign from the first analysis with the expansion surfaces. These are proposed additions; confirm package names against how your scanners report them.
Download the base campaign config
Import it into Phoenix, then add the libraries from the table below to cover the expansion surfaces.
| Filter | Scanner category | Add libraries |
|---|---|---|
| 1 | SCA | duckdb, pyarrow, brotli (RubyGems, < 0.9.1), brotli (PECL, < 0.21.1) |
| 2 | Container | dotnet-runtime, aspnetcore-runtime, duckdb, clickhouse-server, p7zip, p7zip-full |
Keep version fields empty, as in the original campaign, and triage against the tracker tables above. Close items only when the patched workload is confirmed running, and recheck the open list weekly until every vendor in it has shipped.
Regulatory hooks: US, UK and EU
| Regime | Applies to | What it means for CVE-2026-111497 |
|---|---|---|
| CISA KEV and BOD 22-01 (US) | Federal civilian agencies; widely used as a private-sector benchmark | Not listed as of October 24. If CISA adds it, BOD 22-01 sets a fixed remediation deadline for agencies. Watch the catalog. |
| PCI DSS v4.0, requirement 6.3.3 (US, UK, EU) | Organizations handling cardholder data | Critical or high-security patches must be installed within one month of release. Upstream fixes shipped October 21, so in-scope systems need the fix by late November. |
| DORA (EU) | EU financial entities and their critical ICT providers | ICT risk management and third-party oversight cover bundled components inside vendor products. The unpatched list is a third-party ICT risk question for your vendors. |
| NIS2 (EU) | Essential and important entities | Vulnerability handling and supply chain security duties apply to vendored libraries in operated systems. |
| NCSC guidance (UK) | UK organizations | NCSC vulnerability management guidance expects prioritized patching by exposure; the internet-facing and file-ingestion tiers here are the place to start. |
The common thread across all five: an auditor will ask for evidence that you found the vendored copies, not only that you upgraded Node.js.
Frequently asked questions
Is .NET affected by CVE-2026-111497?
Yes. As of October 25, 2026, the .NET runtime bundles Brotli 1.1.0, which is in the affected 1.0.3 to 1.2.0 range. BrotliDecoder, BrotliStream, HttpClient automatic decompression and ASP.NET Core request decompression middleware all use it. No .NET fix had shipped by that date.
Does a Linux distribution update fix HTTP2Shell?
Only for software that dynamically links the system library (libbrotlidec, packaged as libbrotli1 or brotli), and only after the process restarts. Node.js, Bun, .NET, DuckDB, ClickHouse and other products that bundle their own copy need their own release.
Which applications bundle their own Brotli decoder?
Known bundlers include Node.js, Bun, .NET, v8-jsi, GraalJS, DuckDB, ClickHouse, Apache Arrow bundled builds, 7-Zip-zstd, p7zip, NanaZip, EDK II, woff2, MuPDF, libjxl, Chromium, Firefox, Ghostscript, SumatraPDF and Brotli4j. The list is incomplete because private and build-time vendored copies are not searchable.
Can HTTP2Shell be exploited without HTTP?
Yes. Brotli is an internal codec in Parquet, WOFF2 fonts, archives, firmware images, PDF and JPEG XL. Any tool that parses those formats from an untrusted source with a vulnerable decoder runs the bug, with no Content-Encoding header and no WAF in the path.
Is HTTP2Shell being exploited in the wild?
No in-the-wild attacks had been reported as of October 22, 2026, and CISA KEV did not list it as of October 24. Imperva achieved RCE on stock Node.js 24.22.0. The public PoC reaches RCE only on a weakened build and crashes standard builds.
Why does my scanner say a patched server is still vulnerable?
Distributions often backport the fix without changing the upstream version number. Ubuntu 24.04 ships the fix as libbrotli1 1.1.0-2ubuntu0.1. A scanner that compares only the upstream Brotli version reports a false positive. Match on the distribution’s fixed package version.
How do I find a vendored Brotli copy?
Search source trees, including submodules, for Brotli decode.h files and flag any that lack BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH. Prebuilt binaries and wheels need SBOM data or a vendor statement. /proc/*/maps only shows dynamically linked copies.
What is Broccoli2Shell?
Another name for CVE-2026-111497, coined by Juliano Rizzo and used by the broccoli2shell.com tracker. The site states it did not find the bug; credit goes to OpenAI Codex Security, the Node.js maintainers and Imperva.
Which versions fix CVE-2026-111497?
Upstream Brotli 1.0.10, 1.1.1 and 1.2.1. Node.js 22.23.4, 24.22.1, 26.12.1. Bun 1.4.4. PyPI Brotli 1.2.1 and brotlicffi 1.2.1.0. Ruby gem 0.9.1. PHP extension 0.21.1. Envoy 1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12. Istio 1.29.9, 1.30.6, 1.31.2. MuPDF 1.28.0. UEFITool A76.
References
- NVD: CVE-2026-111497
- GitHub Advisory GHSA-9v8h-qqgm-4m35
- Google Brotli fix commit 0ed4268
- Node.js October 2026 security releases
- Red Hat: CVE-2026-111497
- Ubuntu CVE tracker
- Envoy advisory GHSA-3wvj-j6qm-62mr
- Bun issue #46352
- Broccoli2Shell bundled-copy tracker
- Imperva: Node2Shell, from Brotli data to RCE
- OpenSourceMalware: Python packages vulnerable to Node2Shell
- Public PoC: ikow/brotli2crash
- Cloudflare emergency WAF release
- Microsoft: Azure WAF guidance
- oss-security: Brotli CVE-2026-111497
- The Hacker News: Brotli decoder flaw beyond Node.js
- Atlasum: KEV status
- dev.to: patched stack still reporting vulnerable version
- Phoenix Security: HTTP2Shell first analysis
















