# HTTP2Shell: the Brotli vulnerability that also hits WordPress

> HTTP2Shell (CVE-2026-111497, CVSS 9.2) is a Brotli decoder bug that Wordfence exploited in the lab against WordPress via XML-RPC pingbacks, even on patched PHP 8.3.36. Who is exposed, how to check and how to fix it.

- Autore / Author: Diego Betto (https://diegobetto.com/about/)
- Pubblicato / Published: 2026-11-03
- URL: https://diegobetto.com/en/http2shell-brotli-vulnerability-wordpress/
- Tag: wordpress, security, php

Two weeks after [PHP2Shell](/en/php2shell-php-critical-vulnerabilities/), another "2Shell" bug is worrying anyone who runs WordPress. It is called **HTTP2Shell** (also Node2Shell or Brotli2Shell), it is tracked as **CVE-2026-111497** with a **CVSS v4.0 score of 9.2**, and it does not live in WordPress or in PHP: it lives in **Brotli**, Google's compression library that half the web stack uses to decode compressed HTTP responses.

On <time datetime="2026-11-02">2 November 2026</time> Alex Thomas of the Wordfence Threat Intelligence team published an [analysis of what it means for WordPress](https://www.wordfence.com/blog/2026/11/http2shell-wordpress-brotli/): in an isolated lab they achieved **unauthenticated code execution** on a WordPress site through the XML-RPC pingback feature, on a server running **PHP 8.3.36**, the release that already fixes PHP2Shell.

> **Updating PHP is not enough**
>
> The bug is in the system Brotli library, not in PHP or WordPress. A fully updated WordPress on a patched PHP can still be exposed if the server loads a vulnerable `libbrotli`. The fix is to **update Brotli and restart** the processes that use it.

## At a glance

| | |
| --- | --- |
| **Name** | HTTP2Shell, also Node2Shell / Brotli2Shell (it has nothing to do with HTTP/2) |
| **CVE** | CVE-2026-111497 (published by Google) |
| **Severity** | CVSS v4.0 9.2 |
| **Type** | Out-of-bounds write in the Brotli decoder: crash, memory disclosure or RCE |
| **Affected** | Brotli **1.0.3 – 1.0.9**, **1.1.0**, **1.2.0** |
| **Fixed** | Brotli **1.0.10**, **1.1.1**, **1.2.1** (and distribution backports) |
| **WordPress route** | Unauthenticated `pingback.ping` via `xmlrpc.php`, reproduced in the lab by Wordfence |
| **Exploitation** | In CISA KEV since 27 October 2026; scanning and attempts observed, no reviewable report of a production compromise via WordPress |

## What the bug is

Brotli is a compression format, used by browsers and servers as an alternative to gzip (`Content-Encoding: br`). The bug is in `DecodeBlockTypeAndLength()` in the C decoder: a return value is not checked (CWE-252), so the decoder cannot tell "malformed data" from "need more input" and ends up writing **out of bounds** (CWE-787). A crafted compressed stream can corrupt the memory of the process decoding it.

According to [Upwind](https://www.upwind.io/feed/http2shell-cve-2026-111497-one-brotli-bug-rce-across-the-http-stack), the bug was reported by OpenAI in February 2026 and fixed in the code at the time, but no release shipped the fix until October. It surfaced on <time datetime="2026-10-21">21 October</time> as a Node.js bug (hence "Node2Shell"), and it quickly became clear that the flaw is in the shared C library: Node.js, Python packages (`Brotli`, `brotlicffi`), Bun, Envoy, and every program linked against a vulnerable `libbrotli`, curl included, are affected.

## How WordPress gets involved

WordPress does not decompress Brotli by itself. But it makes **outbound HTTP requests**, and the HTTP client underneath (on most servers PHP's curl extension, i.e. libcurl) can hand a `br`-compressed response to the system Brotli decoder. If an attacker can make WordPress **fetch a URL they control**, they can serve a malicious compressed response and reach the vulnerable code from inside the PHP worker.

In stock WordPress, Wordfence found one clear unauthenticated route: **pingbacks**. When someone calls `pingback.ping` on `xmlrpc.php`, WordPress downloads the source page to check that it really links to your post. The attacker supplies that source URL.

For the attack to work, all of these must be true:

- `xmlrpc.php` is reachable;
- the `pingback.ping` method is registered;
- the target post accepts pings;
- the server can make outbound requests;
- the HTTP client uses the vulnerable Brotli decoder;
- the payload matches the PHP build and memory layout of the server.

Other URL-fetching features in core require authentication, extra permissions or control over content. **Plugins and themes can add new routes**, though: anything that fetches a URL supplied by a visitor is a candidate.

> **'Disable XML-RPC authentication' does not stop pingbacks**
>
> Wordfence points out that its own **Disable XML-RPC authentication** option blocks login attempts via XML-RPC, but **does not disable unauthenticated pingbacks**. Do not assume you are covered just because that box is ticked.

## What Wordfence demonstrated in the lab

The test environment was deliberately realistic: isolated containers with **WordPress 7.1.3**, **Debian 13.7**, **Apache 2.4.69**, **PHP 8.3.36 (mod_php)** and `libbrotli1` **1.1.0-2+b7**, with ASLR enabled.

- **Vulnerable Brotli**: the public proof of concept executed code without authentication.
- **Brotli updated** to Debian's fixed package, changing nothing else: the same sequence did not execute code and the server stayed healthy.
- **PHP 8.3.35**: the payload did not execute; in one environment it **crashed the PHP worker**.

The takeaway: the public payload is **not universal**, it depends on the exact build. But servers built from the same images (the same Docker base, the same distribution packages) share the same memory layout, so a working payload can be reused across many of them.

Wordfence also notes that an attacker can usually work out from the outside that a site runs WordPress, which PHP and web server it uses, whether XML-RPC is open and how it makes outbound requests: enough to pick a payload prepared for a common server image. What ordinary requests do not reveal is the exact binary build and memory layout needed to adapt the payload to an arbitrary server, and Wordfence did not demonstrate that. Indiscriminate attacks are therefore less reliable, targeted ones against standard images remain plausible.

## Is it being exploited?

The picture as of <time datetime="2026-11-02">2 November 2026</time>, per Wordfence:

- **CISA** added CVE-2026-111497 to the **Known Exploited Vulnerabilities** catalog on <time datetime="2026-10-27">27 October</time>, without naming the application, route or outcome.
- **Check Point** reports automated scanning and suspicious payloads, no confirmed compromise. **Previdian** keeps observing attempts, but cannot tell whether targets were vulnerable.
- **ELLIO** captured an attempt against Node.js whose payload produced no shell in 50 replays on a matching vulnerable target. **Imperva** and ELLIO demonstrated RCE on vulnerable Node.js builds, in the lab.
- No independently verifiable report of a production compromise, and none via the WordPress XML-RPC route.

Since CISA's entry gives no details, the lack of a public case **does not mean nobody has been compromised**. In short: attempts exist, mass compromise has not been documented. Not a reason to wait.

## Am I exposed?

The WordPress version tells you nothing: what matters is the Brotli library loaded by the process serving the site.

### Managed or shared hosting

Ask your provider two specific questions: does the process serving my site use a **patched Brotli decoder**? Have the services been **restarted or redeployed** since the update?

### Your own server

Check the installed Brotli package. Distributions **backport** the fix without changing the base version, so compare the full revision against your vendor's advisory (e.g. on Debian 13 the fix is in `libbrotli1` **1.1.0-2+deb13u1**; see also [Ubuntu USN-8956-1](https://ubuntu.com/security/notices/USN-8956-1) and [Amazon Linux ALAS2023-2026-3334](https://alas.aws.amazon.com/AL2023/ALAS2023-2026-3334.html)):

```bash
# Debian/Ubuntu
dpkg -l 'libbrotli*'

# RHEL, Alma, Rocky, Amazon Linux
rpm -qa 'brotli*' 'libbrotli*'

# does curl (and therefore PHP's curl extension) support brotli?
curl -V | grep -io brotli
```

To check whether `pingback.ping` is available on your site, use the unauthenticated `system.listMethods` call. It only tells you the method exists, not that the site is exploitable:

```bash
curl -s https://example.com/xmlrpc.php \
  -H 'Content-Type: text/xml' \
  --data '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>' \
  | grep -o 'pingback.ping'
```

## How to fix it

### 1. Update Brotli and restart

Install the vendor's fixed package, then **restart every process that had loaded the old library**: Apache, PHP-FPM, anything else. Until you restart, the old code stays in memory.

```bash
# Debian/Ubuntu
sudo apt update && sudo apt install --only-upgrade 'libbrotli*'
sudo systemctl restart apache2        # if you use mod_php
sudo systemctl restart php8.3-fpm     # adjust to your versions

# processes still using the old, deleted library
sudo lsof 2>/dev/null | grep -i 'libbrotli.*deleted'
```

- **Containers**: rebuild and redeploy from an updated base image. Restarting the existing container is not enough.
- **CloudLinux and similar stacks**: there may be several copies of Brotli (system, web server, bundled PHP). Update each one; CloudLinux has a [dedicated list of packages](https://cloudlinux.zendesk.com/hc/en-us/articles/31345594031132-Brotli-CVE-2026-111497-on-CloudLinux-OS-affected-packages-and-fixed-versions).
- **Agencies and hosting providers**: check every environment, production, **staging** and legacy alike. One package check does not cover every PHP pool, container or server image.
- **Node.js on the same server**: fixed in 22.23.4, 24.22.1 and 26.12.1. Lines 20, 23 and 25 are end-of-life and will not get a fix.

### 2. Close the WordPress route (as a stopgap)

If you do not use pingbacks (and almost nobody does), turning them off removes the route demonstrated by Wordfence. It **does not fix the library**, it only removes one path to it. A mu-plugin is the quickest way:

```php
<?php
// wp-content/mu-plugins/disable-pingbacks.php
add_filter('xmlrpc_methods', function (array $methods): array {
    unset($methods['pingback.ping'], $methods['pingback.extensions.getPingbacks']);
    return $methods;
});
```

Note that the `xmlrpc_enabled` filter, often suggested as the way to "turn off XML-RPC", only affects methods that require authentication: per the WordPress docs it **does not disable `pingback.ping`**. That is why the snippet above removes the method explicitly.

If you do not need XML-RPC at all (no Jetpack, no legacy mobile app), you can block `xmlrpc.php` at the web server level. Remember that **Settings → Discussion** only applies to future posts: existing posts keep their own ping setting. And check before switching things off: changing XML-RPC or pingback behaviour can break legitimate integrations.

### 3. Check for signs of compromise

- **Preserve logs** (web server, PHP, processes, firewall, outbound traffic) from before the disclosure until the patch and restart.
- Look for **unexpected pingback requests**, **unusual outbound connections** from the PHP user, and **crashes or restarts** of PHP or Apache workers. Their absence does not prove nothing happened.
- If you suspect a compromise, **contain the system first**, then rotate credentials, keys and salts. In the reverse order, an attacker still inside sees the new secrets.

> **Do not run PoCs you found on GitHub**
>
> As with PHP2Shell, there is a public WordPress proof of concept, and plenty of fake ones. Do not run them on production: if you want to test, use an isolated environment and code you have read line by line.

## Timeline

- **February 2026** — OpenAI reports the bug; Brotli fixes the code, but without a release.
- **<time datetime="2026-10-21">21 October 2026</time>** — Node.js discloses and patches the bug ("Node2Shell"); Brotli 1.0.10, 1.1.1 and 1.2.1 are released.
- **<time datetime="2026-10-22">22 October 2026</time>** — Google publishes CVE-2026-111497; a GitHub advisory confirms the Python `Brotli` and `brotlicffi` packages are affected.
- **<time datetime="2026-10-23">23 October 2026</time>** — The bug is widely nicknamed HTTP2Shell.
- **<time datetime="2026-10-27">27 October 2026</time>** — CISA adds it to the KEV catalog.
- **<time datetime="2026-11-02">2 November 2026</time>** — Wordfence publishes its WordPress analysis and lab results.

## Sources

- [Wordfence — HTTP2Shell: What the Critical Brotli Vulnerability Means for WordPress Sites](https://www.wordfence.com/blog/2026/11/http2shell-wordpress-brotli/)
- [CVE.org — CVE-2026-111497](https://www.cve.org/CVERecord?id=CVE-2026-111497)
- [GitHub — Brotli 1.2.1 release](https://github.com/google/brotli/releases/tag/v1.2.1)
- [GitHub — Upstream Brotli fix](https://github.com/google/brotli/commit/0ed4268a4401abcb4472096340477cf4af7d1a2b)
- [CISA — Known Exploited Vulnerabilities catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-111497)
- [Debian Security Tracker — CVE-2026-111497](https://security-tracker.debian.org/tracker/CVE-2026-111497)
- [Ubuntu — USN-8956-1](https://ubuntu.com/security/notices/USN-8956-1)
- [Amazon Linux — ALAS2023-2026-3334](https://alas.aws.amazon.com/AL2023/ALAS2023-2026-3334.html)
- [Upwind — HTTP2Shell: one Brotli bug, RCE across the HTTP stack](https://www.upwind.io/feed/http2shell-cve-2026-111497-one-brotli-bug-rce-across-the-http-stack)
- [Check Point — HTTP2Shell: the Brotli vulnerability that extends beyond Node.js](https://blog.checkpoint.com/security/http2shell-the-brotli-vulnerability-that-extends-beyond-node-js/)
- [Tenable — Brotli2Shell FAQ](https://www.tenable.com/blog/brotli2shell-frequently-asked-questions-for-google-brotli-decoder-cve-2026-111497)
- [WordPress Developer Resources — xmlrpc_enabled](https://developer.wordpress.org/reference/hooks/xmlrpc_enabled/)
- [WordPress Developer Resources — pingback_ping()](https://developer.wordpress.org/reference/classes/wp_xmlrpc_server/pingback_ping/)

If you have not done it yet, also go through my [WordPress security checklist](https://diegobetto.com/en/wordpress-security-checklist/), and make sure the server is on a PHP release that fixes [PHP2Shell](/en/php2shell-php-critical-vulnerabilities/): the two bugs are independent, and you need both fixes.
