WordPress does not ship a Brotli decoder, but every outbound request it makes through libcurl advertises br and gets decoded by the system libbrotli. That is how CVE-2026-111497, the Brotli out-of-bounds write on CISA’s exploited list since 27 Oct (federal deadline 10 Nov), reaches a WordPress site. The fix is the OS package: on Ubuntu 24.04 that is libbrotli1 1.1.0-2ubuntu0.1 or later, followed by a PHP-FPM restart. Until that is done, a 15-line must-use plugin stops libcurl from decoding Brotli and removes the unauthenticated pingback.ping route that lets anyone make your site fetch a URL.
We tested everything below on WordPress 7.1.4, PHP 8.3.6, libcurl 8.5.0 and MariaDB 10.11.14 in an Ubuntu 24.04 Linux container on 2 Nov 2026, against a local test server that returns a harmless Brotli-compressed body. Nothing here touches a malformed stream.
Why we did this: the Paperkite rebuild runs WordPress on our CloudPanel VPS, and our first Brotli audit on 27 Oct (Broccoli2Shell: auditing brotli on a VPS) covered Node, Python and the shared library, but not how WordPress itself ends up in the decoder. Patchstack’s write-up on 30 Oct made that path concrete, so we reproduced it.
The bug and the fixed versions
CVE-2026-111497 is an out-of-bounds write in Google’s Brotli C decoder, versions 1.0.3 to 1.2.0, fixed upstream in 1.0.10, 1.1.1 and 1.2.1 on 21 Oct 2026. Distros backport the patch into their existing version, so the upstream number lies. On our Ubuntu 24.04 image the package changelog shows it plainly:
zcat /usr/share/doc/libbrotli1/changelog.Debian.gz | head -6
brotli (1.1.0-2ubuntu0.1) noble-security; urgency=medium
* SECURITY UPDATE: DoS or code exec via buffer overflow
- debian/patches/CVE-2026-111497.patch: Detect and report invalid stream
earlier. in c/dec/decode.c, c/include/brotli/decode.h.
- CVE-2026-111497
So on Noble, 1.1.0-2ubuntu0.1 (dated 25 Oct) is the patched build even though it still says 1.1.0. Check your own with dpkg-query -W libbrotli1 and read the changelog rather than the version.
For PHP behind FPM, Patchstack reports the reliable effect as crashed or pinned workers, which is downtime; code execution is not established for that setup.
How WordPress reaches the decoder
WordPress makes outbound requests through WP_Http, the bundled Requests library and its cURL transport. That transport sets CURLOPT_ENCODING to an empty string, which tells libcurl to offer every encoding it was built with and to decode the response inside curl_exec(). PHP’s curl extension links the system library directly:

Real output. Without the mu-plugin, WordPress sent Accept-Encoding: deflate, gzip, br, zstd and got clear text back from a Brotli body. With it, the request asked for identity and the 38 bytes came back undecoded.
Two details from that run are worth spelling out:
- Our test server ignores
Accept-Encodingand sends Brotli anyway, the way a hostile server would. Asking foridentityalone is not enough;CURLOPT_HTTP_CONTENT_DECODING = 0is what keeps libcurl from decoding a response it did not ask for. That is why the snippet sets both. - Passing
decompress => falsetowp_remote_get()does not help. Patchstack points out the decode already happened inside libcurl by then.
The mu-plugin we tested
Save as wp-content/mu-plugins/no-brotli.php. Must-use plugins load on every request and cannot be switched off from the dashboard.
<?php
/**
* Plugin Name: No Brotli in outbound HTTP (CVE-2026-111497 stopgap)
* Keeps libcurl from decoding responses, and removes the unauthenticated pingback fetch path.
*/
add_action( 'http_api_curl', function ( $handle ) {
curl_setopt( $handle, CURLOPT_ENCODING, 'identity' );
curl_setopt( $handle, CURLOPT_HTTP_CONTENT_DECODING, 0 );
}, PHP_INT_MAX );
add_filter( 'xmlrpc_methods', function ( $methods ) {
unset( $methods['pingback.ping'], $methods['pingback.extensions.getPingbacks'] );
return $methods;
} );
The cost of the first half: any remote server that compresses regardless of what you ask for will now hand you compressed bytes. In our run a normal wp_remote_get() of the site’s own RSS feed still returned HTTP 200 with 1,621 bytes of XML, and fetch_feed() still parsed it (1 item, which is all a fresh install has). Most servers honour identity, but test the plugins that call third-party APIs before leaving this on for weeks.
Pingback: the unauthenticated way in
pingback.ping over xmlrpc.php makes WordPress fetch the source URL a caller gives it, with no login, on any install with a pingable post. That is the route anyone on the internet can use to point your site at a server they control. We sent a system.listMethods call and a pingback.ping call to the local site before and after adding the mu-plugin:

Fault 0 means WordPress accepted the call and ran the method (our dummy target simply wasn’t a valid pingback target). Fault -32601 means the method no longer exists.
Note that the commonly copied add_filter( 'xmlrpc_enabled', '__return_false' ) does not do this: per Patchstack it switches off the authenticated XML-RPC methods and leaves pingback alone. If nothing on the site uses XML-RPC (no Jetpack, no old mobile app), denying xmlrpc.php at nginx is cleaner still, but it only closes this one route, not plugins that fetch user-supplied URLs, oEmbed discovery or feeds.
The order of operations
- Install the patched
libbrotli1(on Ubuntu 24.04,1.1.0-2ubuntu0.1or later):sudo apt update && sudo apt install --only-upgrade libbrotli1. - Restart PHP-FPM. Running workers keep the old library mapped until they restart, so updating the package alone is not enough. A worker still on the old copy shows
libbrotlidecas(deleted)in/proc/<pid>/maps. - Add the mu-plugin as a stopgap where you cannot patch the OS today (shared hosting, a client’s box), then remove the encoding half once the library is updated.
- Watch the logs for PHP-FPM children exiting on signal 11 and for unauthenticated
POST /xmlrpc.phpwithpingback.pingfrom unfamiliar sources.
We did not test steps 2 and 4 in this container, because there was no vulnerable library build to compare against: the image already had the patched package. They follow from how shared libraries and the advisory’s detection notes work.
Sources: Patchstack, “WordPress doesn’t ship Brotli. It’s still exposed to CVE-2026-111497” (30 Oct 2026); the Ubuntu libbrotli1 package changelog (25 Oct 2026); CISA KEV entry dates as reported by Patchstack.



