p.enthalabs

Broccoli2Shell: Brotli decoder out-of-bounds write (CVE-2026-111497)

Who's exposed

You're at risk if your code, or any library it uses, decompresses Brotli data that someone else controls. That happens in more places than you'd think, because HTTP clients decompress Brotli responses on their own.

- **Your app fetches a URL**

Node's built-in `fetch()` decodes `Content-Encoding: br` responses automatically. So do Python's `requests`/`urllib3` and `aiohttp` whenever a Brotli package is installed. Any server you talk to can send the payload, whether through webhooks, link previews, crawlers or "import from URL" features.

- **Your app accepts uploads or request bodies**

Express 5.0.0–5.3.0 and body-parser 2.0.0–2.3.0 inflate Brotli request bodies in parsers such as `raw()` and `json()`. Python frameworks and middleware that decode `br` bodies are exposed in the same way. A bare `node:http` server doesn't decode bodies by itself.

- **Your app calls the decoder directly**

This means `zlib.brotliDecompress*()`, `createBrotliDecompress()`, `brotli.decompress()`, and any library that uses them on archives, caches or queue messages.

Express and body-parser don't contain the bug. They're just paths to it, so upgrading them won't help. Node.js bundles its own copy of Brotli, which means the system `libbrotli` doesn't matter there. Only the Node.js version does.

The bug

A Brotli stream is split into blocks, and the decoder switches between block types as it reads them. Before the fix, `DecodeBlockTypeAndLength()` in `c/dec/decode.c` returned a plain boolean, so callers couldn't tell a malformed stream from one that just needed more input. On the fast path, the decoder ignored the return value altogether.

A crafted stream can request a block switch that can't happen. The decoder took that to mean "need more input" and kept adding attacker bytes to an eight-byte internal buffer, writing past its end. The overwrite corrupts nearby decoder state. That's how a malformed stream can turn into a memory leak or code execution.

The upstream fix is google/brotli commit `0ed4268`, along with `50a9b70`, `b418fa8` and `da98063`. It returns a hard format error, `BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH` (−17), to the caller. The bug is tracked as CWE-252 (unchecked return value) and CWE-787 (out-of-bounds write).

Python

The PyPI packages `Brotli` and `brotlicffi` compile the same C decoder. Your code doesn't need to import them to be exposed. If either one is installed, these clients use it on their own:

- **urllib3**, and therefore **requests**, advertises `Accept-Encoding: br` and decodes Brotli responses.

- **aiohttp** uses Brotli when it's installed.

- **httpx** uses it through the `httpx[brotli]` extra.

- **Scrapy** and other crawlers. Some versions install Brotli as a dependency.

Don't wait for your scanner. When this was disclosed, pip-audit, Dependabot and Safety didn't reliably flag the affected PyPI versions. The CVE record named the upstream Google repository, not the PyPI packages. GitHub's advisory now lists both packages (PYSEC-2026-4322 and PYSEC-2026-4323), but check by hand anyway.

If your bindings come from your Linux distribution (for example `python3-brotli`) and not from PyPI, the fix arrives through your distribution's security updates.

Other software

The bug is in Google's reference C decoder, not in Node.js or Python. Anything that ships or links Brotli 1.0.3 through 1.2.0 may be affected, even before an advisory names it.

- **The system library.** It's called `libbrotlidec`, packaged as `libbrotli1`, `brotli` and similar. Programs that link it dynamically are fixed once your distribution ships a patched package and you restart them. Statically linked or vendored copies need their own rebuild.

- **Bindings that wrap the C code.** This covers Java JNI wrappers, PHP and Ruby extensions, and Go cgo bindings. Look for a release that bundles 1.0.10, 1.1.1 or 1.2.1.

- **Proxies, CDNs, HTTP clients and archive tools** that decode `br` content with the C library.

Firewalls that inspect decrypted traffic

**Palo Alto Networks PAN-OS** 11.2, 12.1 and 12.2 on PA-Series and VM-Series firewalls are affected when TLS/SSL decryption and Brotli inspection are both enabled. Then the firewall decodes Brotli traffic passing through it on its dataplane, so any web server that a user behind the firewall talks to can send the payload, and the target is the firewall itself. Palo Alto rates it 9.2 Critical. PAN-OS 11.1 and 10.2 aren't affected, because Brotli inspection only arrived in 11.2.4. Panorama and the management interface aren't affected either.

PAN-OS hotfixes, as of 27 October | Branch | Available | Due | | --- | --- | --- | | 12.2 | 12.2.4-h1 | None | | 12.1 | 12.1.7-h11 | 12.1.4-h13 on 29 Oct, 12.1.10-h3 on 30 Oct | | 11.2 | 11.2.10-h17 | 11.2.13-h4 on 27 Oct, 11.2.16-h1 on 28 Oct, 11.2.7-h23 on 29 Oct, 11.2.4-h23 on 30 Oct |

Watch out for 11.2.10-h16: it fixes a separate PHP issue but not this one. Until you can install a hotfix, check with `show system setting brotli` and turn inspection off with `set system setting brotli-encoding disable`, or untick "Allow Brotli Encoding" under Device > Setup > Content-ID. Prisma Cloud's Application Security Transporter Client is affected too, and is fixed in 0.2.9. See Palo Alto's advisory.

FFmpeg, through JPEG XL images

Since FFmpeg 8.1 (March 2026), its libjxl-based JPEG XL decoder reads Exif metadata and asks libjxl to decompress Brotli-compressed metadata boxes. A crafted `.jxl` image therefore reaches the Brotli decoder in any FFmpeg built with `--enable-libjxl`. That covers thumbnailers, media servers and upload pipelines that run FFmpeg on files users send. FFmpeg doesn't contain Brotli itself. The fix is in whichever libbrotli libjxl was linked against. Distribution packages are fixed by your system's Brotli update. Static builds carry their own copy, so check the build's Brotli version. BtbN's builds have pinned a patched Brotli commit since 27 September.

Decoders rewritten in other languages aren't a concern. The Java, JavaScript and C# ports, which pdf.js, iText and HtmlUnit use, can't reach the bug. The Go port (andybalholm/brotli) had the same logic error, but it would only have crashed, and it was fixed on 23 October. Rust's `brotli-decompressor` catches the condition and returns an error. Code execution is only possible in the C decoder.

Projects that bundle their own copy

Many projects ship a private copy of the C decoder instead of using your system's library. A distribution update doesn't fix those: each project has to update its copy. We searched GitHub for vendored copies and read their headers, and last rechecked them on 27 October 2026. A copy is patched if it includes the new `BLOCK_SWITCH` error code.

Not fixed yet, as of 27 October | Project | Bundled copy | Untrusted input it decodes | | --- | --- | --- | | DuckDB | unpatched | Brotli-compressed Parquet files. A fix is in progress in PR #27791. |

| Apache Arrow (bundled build) | 1.0.9 | Brotli-compressed Parquet files. Tracked in issue #55166. PR #55167, which moves to Brotli 1.0.10, has been approved. It's waiting on fixes to wheel builds. | | v8-jsi | 1.2.0 | The same paths as Node.js, because it bundles Node.js | | 7-Zip-zstd | 1.2.0 | Archives and `.br` files a user opens. Tracked in issue #579. | | p7zip | 1.0.9 | Archives and `.br` files a user opens | | EDK II | 1.2.0 | Compressed firmware sections. Tracked in issue #13378. |

| woff2 (Makefile build) | 1.0.3 | WOFF2 fonts passed to `woff2_decompress`. The CMake build links the system library instead. Tracked in issue #207. A fix has been proposed in PR #208. |

Fixed since disclosure | Project | Fixed in | Details | | --- | --- | --- | | Ruby `brotli` gem | 0.9.1 | Bundles Brotli 1.2.1 and refuses to build against an unpatched system libbrotli. Released on 24 October (issue #64). |

| .NET runtime | 8.0.33, 9.0.22, 10.0.14 | Also 11.0.0-rc.3. Covers `BrotliStream`, `BrotliDecoder`, `HttpClient` automatic decompression and ASP.NET Core request decompression. Released on 25 October (advisory). The public main branch moved to Brotli 1.1.1 on 26 October (PR #136471). | | PHP brotli extension | 0.21.1 | Bundles Brotli 1.2.1. Released on 24 October (PR #105). | | UEFITool | A76 | Bundles Brotli 1.2.1. Released on 24 October (issue #492). | | NanaZip | 7.0.1882.0 | Bundles Brotli 1.2.1. Released on 26 October (issue #1034). |

| Varnish Enterprise | 6.0.18r7 | Closed source, but it bundles its own Brotli for the brotli VMOD, which decodes backend responses. A check of the official Docker images found a patched copy in 6.0.18r7 and a few releases before it. We don't know the first fixed release, and Varnish Software hasn't published an advisory. | | MuPDF | 1.28.0 | Its Brotli fork picked up the upstream fix in April 2026, before disclosure, so every 1.28.x release is patched. The 1.27.x releases still bundle the affected decoder. |

| ClickHouse | master branch | Updated to Brotli 1.2.1 in PR #127419 on 25 October and backported to 26.10, 26.9, 26.8 and 26.3 LTS. Not in a release yet: the latest, 26.10.2.103, came out before the fix. | | GraalJS (graal-nodejs) | master branch | The fix was merged on 22 October. Not in a release yet: GraalVM ships security updates quarterly, and the latest release, 25.4.4.1.1, came out before the fix. | | YDB | main branch | Patched with the upstream fix in 343f6be on 23 October. Not in a release yet. | | libjxl | main branch | Updated to Brotli 1.2.1 in PR #5062 on 24 October. Not in a release yet: the latest, v0.12.0, came out before the fix. |

**Fixed before this list was made:** Node.js, Bun, Chromium, Firefox, brotlicffi, ngx_brotli, Brotli4j, SumatraPDF, Ghostscript, CatBoost and YTsaurus, as well as Gentoo, Solus and VMware Photon packages.

This list isn't complete. GitHub's code search skips submodules and some very large repositories, and private or vendored-at-build-time copies don't show up at all. A project listed here may not reach the decoder in every configuration. If you maintain one of these projects, updating to 1.0.10, 1.1.1 or 1.2.1 (or cherry-picking commit `0ed4268`) fixes it.

Check your versions

Node.js

``` node --version

affected if below 22.23.4, 24.22.1 or 26.12.1, or on an end-of-life line

the Brotli version bundled into this Node binary

node -p "process.versions.brotli"

inside a container image

docker run --rm --entrypoint node <image> --version ```

Python

``` pip show Brotli brotlicffi

what pulls it in

pipdeptree -r -p Brotli

lockfiles

grep -i brotli poetry.lock uv.lock requirements*.txt Pipfile.lock 2>/dev/null

inside a container image

docker run --rm --entrypoint pip <image> show Brotli brotlicffi ```

System library

``` dpkg -l | grep -i brotli # Debian, Ubuntu rpm -qa | grep -i brotli # RHEL, Fedora apk info -v | grep -i brotli # Alpine

running processes that have the decoder loaded

sudo grep -l brotlidec /proc/*/maps 2>/dev/null ```

Check everywhere code runs: production, staging, CI runners, build images, serverless runtimes and developer laptops. Patch first any service that fetches URLs you don't control or accepts `Content-Encoding: br` bodies.

Fix it

Upgrade

```

Node.js: through your version manager, base image or package source

nvm install 24.22.1 # or 22.23.4 / 26.12.1 FROM node:24.22.1-slim # Dockerfile

Python

pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0"

then raise the constraint and regenerate poetry.lock / uv.lock / requirements.txt

```

Then restart every process, rebuild every image and redeploy. Update the pins in your lockfiles, or the next build will reinstall the vulnerable version.

If you can't upgrade yet

- **Python:** uninstall `Brotli` and `brotlicffi`. urllib3, requests and aiohttp will fall back to gzip and deflate. Remove extras such as `httpx[brotli]` too, or the next install brings the package back.

- **Inbound:** reject `Content-Encoding: br` request bodies at your proxy, load balancer or WAF. Several providers, including Vercel, deployed WAF rules for this on disclosure day.

- **Outbound:** removing `br` from `Accept-Encoding` helps less than you'd hope, because a malicious server can send Brotli anyway. A WAF doesn't see responses your app fetches. Only the runtime or library upgrade covers that path.

Node.js has published no workaround. For Node.js, upgrading the runtime is the only complete fix.

Exploitation status

- **Imperva Red Team** ran attacker-supplied code on the official, unmodified Node.js 24.22.0 Linux arm64 image. They delivered the payload both as a fetched response and as an Express request body. It took several attempts, and they haven't released the exploit.

- A **public proof of concept** appeared on GitHub on 2026-10-22. It reportedly reaches code execution only on a deliberately weakened build. On standard builds it causes a crash and a limited memory read.

- For **Python**, researchers confirmed the bug with AddressSanitizer. Version 1.2.0 accepted a crafted stream of about 23 MB, and 1.2.1 rejected it with error −17. They didn't attempt an exploit.

- As of 2026-10-22, no attacks in the wild had been reported. With public PoCs out, expect that to change.

Timeline

1. Feb 2026 The fix first lands in the Brotli source, per OpenSourceMalware's analysis. 2. 21 Oct 2026 Node.js releases 22.23.4, 24.22.1 and 26.12.1. Google releases Brotli 1.0.10, 1.1.1 and 1.2.1. 3. 22 Oct 2026 NVD publishes CVE-2026-111497 and GitHub publishes GHSA-9v8h-qqgm-4m35. Brotli 1.2.1 reaches PyPI, Imperva publishes its research, and a public PoC appears. 4. 23 Oct 2026 brotlicffi 1.2.1.0 reaches PyPI. Researchers publish the Python impact analysis, and GitHub adds the PyPI packages to its advisory. 5. 25 Oct 2026 Microsoft releases .NET 8.0.33, 9.0.22 and 10.0.14. 6. 26 Oct 2026 Palo Alto Networks confirms PAN-OS firewalls are affected and starts shipping hotfixes.

Sources

- Node.js: October 2026 security releases

- GitHub Advisory GHSA-9v8h-qqgm-4m35

- NVD: CVE-2026-111497

- CVE.org record

- google/brotli releases 1.0.10, 1.1.1 and 1.2.1

- Imperva: From Brotli data to remote code execution

- OpenSourceMalware: Python packages are affected too

- Aikido: Python packages are also vulnerable

- Palo Alto Networks: CVE-2026-111497 impact on PAN-OS

- Microsoft: .NET security advisory for CVE-2026-111497

- Vercel: Node.js security advisory