# CVE-2026-111497 (Node2Shell): what the edge does and what you must still patch

> The edge refuses Brotli-encoded request bodies with shield 1009. It doesn't patch your servers or the responses your code fetches; update Brotli.

CVE-2026-111497, also called Node2Shell, HTTP2Shell or Brotli2Shell, is an out-of-bounds write in Google's C Brotli decoder (`DecodeBlockTypeAndLength` in `c/dec/decode.c`). It is fixed in Brotli 1.2.1. A crafted Brotli stream can give an attacker remote code execution in any process that decodes it. It has a CVSS score of 9.2, it has been in the CISA Known Exploited Vulnerabilities catalog since 2026-10-27, and it is being exploited.

You are exposed in two ways:

- **Servers that decode request bodies** sent with `Content-Encoding: br`: Express 5 / body-parser 2.x (`raw()`, `json()`…) on Node.js before 22.23.4, 24.22.1 and 26.12.1, ASP.NET Core request decompression, the ClickHouse HTTP interface, Python services that use `brotli` or `brotlicffi`, and the PHP `brotli` extension.
- **HTTP clients that decode Brotli responses**: Node.js `fetch()`, .NET `HttpClient`, and Python `requests`, `httpx`, `aiohttp` or `urllib3` when `brotli` is installed. A server the attacker controls only has to answer with a crafted body.

## What the edge does

The edge has a new [shield](https://developers.openwaf.dev/waf/shields/#brotli-request-body), **1009 «Brotli Request Body»**. It refuses every request that has a body whose `Content-Encoding` lists `br`, before the request reaches your origin. The header is matched in any case, and `br` is found anywhere in the list: `gzip, br` or across several `Content-Encoding` lines. The request gets a `403` with your [block page](https://developers.openwaf.dev/waf/block-page/). The edge doesn't decode request bodies to look for the exploit.

Nothing else changes:

- requests without a body, and bodies encoded with `gzip`, `deflate`, `zstd` or `identity`, pass as before;
- responses aren't touched;
- browsers never send Brotli request bodies, so visitors aren't affected.

Like every shield, 1009 follows the site's [mode](https://developers.openwaf.dev/waf/modes-and-levels/#modes): it blocks in `active`, only records in `log` (the request reaches the origin) and doesn't run when the WAF is `off`.

The edge's own Brotli decoder is a Go port. It has the same logic bug but no memory corruption (in Go it was a crash, GHSA-c9gr-gmg4-2vqv). It has been updated, and an error while decoding now only ends that stream.

## If your application receives Brotli request bodies

Some APIs legitimately accept bodies compressed with Brotli, for example an ingestion endpoint fed by your own agents. Patch that origin first ([below](#patch)), then add a [WAF exception](https://developers.openwaf.dev/waf/waf-exceptions/) for rule **1009** on that path only, limited to the name that needs it. An exception with `rule_id` `0` does **not** switch the shield off.

**Dashboard**

Domain → **Security → Firewall → Rules → WAF exceptions** → **Add WAF exception**. Enter `1009` as the **WAF rule**, the **Path** (for example `/v1/ingest`), the name in **Applies to** and a **Note**.
**CLI**

```sh
openwaf-cloud ctl rules add example.com kind=waf_exception rule_id=1009 path_pattern='/v1/ingest' \
  hosts:='["api.example.com"]' note="Patched origin, accepts br bodies" --reason "CVE-2026-111497: br bodies on /v1/ingest"
```
**API**

```sh
curl -sS -X POST "https://dash.openwaf.dev/v1/orgs/$ORG/zones/example.com/rules" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "X-Change-Reason: CVE-2026-111497: br bodies on /v1/ingest" \
  -H "Content-Type: application/json" \
  -d '{"kind": "waf_exception", "rule_id": 1009, "path_pattern": "/v1/ingest", "hosts": ["api.example.com"], "note": "Patched origin, accepts br bodies"}'
```
**MCP**

`add_rule` with `{"zone": "example.com", "kind": "waf_exception", "rule_id": 1009, "path_pattern": "/v1/ingest", "hosts": ["api.example.com"], "reason": "CVE-2026-111497: br bodies on /v1/ingest"}`; simulate, then confirm.


> **Caution**
>
> An exception for 1009 sends Brotli bodies on that path straight to your origin. Add it only for an origin that is already patched.


## What the edge doesn't cover

- **Responses your code fetches.** If your servers or scripts call `fetch()` or another HTTP client on a URL an attacker can choose, the crafted Brotli response goes straight to them. The edge only sees requests to your proxied names.
- **Traffic that skips the edge.** Anyone who reaches your origin directly, by its IP address or another name, isn't inspected. Accept connections only from the OpenWAF edge and use [Full (strict)](https://developers.openwaf.dev/ssl/full-strict/) origin TLS.
- **Names that aren't proxied**, sites in `log` mode and sites with the WAF `off`.
- **Other ways of feeding Brotli data** to your software: uploaded files, queues, message brokers, files in storage.

## What you must patch

| Software | Fixed in |
|---|---|
| Node.js | 22.23.4, 24.22.1 or 26.12.1 and later. Lines that are end-of-life get no fix: move to a supported line. Check `node -v` inside your container images too, not just on the host. |
| Python `brotli` / `brotlicffi` | The releases built on Brotli 1.2.1. |
| PHP | The php.net releases of 2026-10-26. |
| .NET | The October 2026 servicing update. |
| `libbrotli` from the OS | Your distribution's security update; then restart the services that load it. |
| Anything else that embeds Brotli before 1.2.1 | Its patched release: FFmpeg, libjxl, ClickHouse, Apache Arrow and others. |

## Check whether you were targeted

Refused requests appear in [security events](https://developers.openwaf.dev/analytics/security-events/) with `rule_id` `1009` and `rule_name` `Brotli Request Body`. In **Analytics › Traffic → Security events**, select **Add filter** → **Rule** *is* `1009`, or ask the API:

```sh
curl -s "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/events?rule_id=1009&since=2026-10-27T00:00:00Z" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

An event before you patched only tells you that someone tried through the edge. It doesn't tell you whether your origin was reached another way. Check the origin's own logs too.

## Sources

- [Node.js: October 2026 security releases](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases)
- [Google Brotli: fix commit 0ed4268](https://github.com/google/brotli/commit/0ed4268a4401abcb4472096340477cf4af7d1a2b)
- [Imperva: Node2Shell, from Brotli data to remote code execution](https://www.imperva.com/blog/node2shell-from-brotli-data-to-remote-code-execution/)
- [JFrog: Brotli2Shell, beyond Node2Shell and HTTP2Shell](https://research.jfrog.com/post/brotli2shell-beyond-node2shell-and-http2shell/)
- [Sophos: Node2Shell in active exploitation](https://www.sophos.com/en-us/blog/node2shell-cve-2026-111497-in-active-exploitation)
