Security

Ubuntu Had No Fix for the Brotli Bug, So We Built One

A memory-corruption bug in Brotli, a compression format used across the web, can be triggered by a malicious compressed response. Ubuntu has no fixed package yet. Here's where the bug actually reached our platform, how we patched every site in one evening, and what to check on your own servers.

A few days after PHP2Shell, another one. This time the bug isn't in PHP. It's in Brotli, a compression format that web servers and browsers use to make pages smaller and faster to download.

CVE-2026-111497 is a buffer overflow in Brotli's decoder, the part that unpacks compressed data. Give it a specially crafted stream and it writes past the end of the memory it was given. That can crash the program, leak whatever was in that memory, or, in the worst case, let an attacker run their own code. Every Brotli release from 1.0.3 through 1.2.0 is affected; the fixed releases are 1.0.10, 1.1.1 and 1.2.1.

Node.js shipped a fix for its own bundled copy of Brotli on 21 October. Debian has since fixed its two current releases. Ubuntu published the CVE on 23 October, rated it Medium priority and, at the time of writing, still lists every release as "needs evaluation", with no fixed package. Our platform runs on Ubuntu.

If you host with us, there is nothing you need to do: every site is already running a fixed copy. The rest of this post is what we did on 24 October and what we'd check if we ran your servers.

Where a compression bug actually reaches a website

Most write-ups of this bug focus on Node.js and on servers that accept compressed uploads. Had we stopped there, we'd have concluded we were barely affected. No Node.js app was running on our platform at the time, and the Node.js we provide uses the shared system library anyway. Our web servers never unpack compressed data from visitors; they only send out files that were compressed in advance.

But a website doesn't only answer requests. It makes them too. A WordPress site checks for plugin updates and fetches link previews. A shop talks to its payment provider. A Laravel app calls APIs and sends webhooks. Most of that traffic goes through curl, the HTTP library PHP uses, and in the way WordPress and Laravel use it, curl automatically unpacks a Brotli response when one comes back.

So the real question wasn't "do we run Node.js?" It was "could any server our sites talk to send back a malicious Brotli response?" Some of the addresses a site fetches come from outside, such as a link someone pastes into a post. For those, the honest answer is yes.

Upgrading Node.js wouldn't have closed that. Neither would removing a Python package. The fix had to go into the Brotli library itself, which PHP's curl and everything else on the machine share.

24 October, step by step (UTC)

The upstream fix had been public for three days and Ubuntu's advisory for one when we started. Here's the evening, from first check to last site.

17:58. We started taking inventory. For every server and container on the platform we checked which Brotli version is installed, which running programs actually have it loaded in memory, and which language runtimes link to it. A library sitting on disk doesn't make a server exposed. A program using it does, and we wanted that list.

18:10. We had it. Every site's PHP loads the vulnerable decoder through curl. That covers customer sites, our control panel and its background workers. We found nothing else on the platform that decodes Brotli data from untrusted sources.

18:18. We had a fixed build. Rather than wait for Ubuntu, we took Ubuntu's own Brotli package and applied the upstream patch, a small change that makes the decoder stop with an error on the malformed input instead of carrying on and writing past its buffer. The rebuilt library passed Brotli's full upstream test suite. We also checked that the library actually loaded on the system contains the fix, rather than trusting a version number. And we numbered our build so that Ubuntu's official update will replace it automatically when it ships.

18:23. The fix was live on our staging environment. We restarted PHP there and checked that it still fetches and unpacks a real Brotli-compressed web page (1.3 MB) correctly.

18:27 to 18:32. We rolled it out to production, one site at a time. For each site we restarted only the programs still holding the old library in memory, confirmed none were left, and checked that the site still responded normally. Any failed check would have stopped the rollout. None failed.

18:33. Every site on the platform was running the fixed decoder, with no downtime.

That's 35 minutes from first check to last site. Most of it went on working out exactly where the bug could reach us; the patching itself took minutes.

Did anyone get in first?

Memory corruption tends to crash the program it hits, so attempts to exploit a bug like this often leave crashes behind. We went through our logs as far back as they go, to 8 October, about two weeks before the fix was published, looking for crashed programs, segmentation faults and memory-protection errors in the system logs, crash dumps, and any mention of segmentation faults or Brotli in PHP's and our applications' logs.

All of it came back clean, and we have found no public reports of this bug being exploited. We found no sign that anyone used it against our platform. To be clear about the limits of that: our access logs record the requests that come in, but not their headers, and not the content of the responses our sites download from elsewhere. A carefully built attack that crashed nothing would not show up in a crash log. That gap is part of why we're expanding our logging, as we wrote after PHP2Shell.

What you need to do

If you host with veldhost: nothing. Every site on our platform is already running the fixed library, and Ubuntu's official update will replace our build automatically when it arrives. If you'd like the details for your own site, ask us.

If you run servers yourself:

  • Don't assume Node.js is the only problem. Check what has the Brotli library loaded. On Linux, sudo grep -l libbrotlidec /proc/*/maps lists the processes using the system copy right now; without root you'll only see your own. PHP (through curl) is a common one. Software that bundles its own copy of Brotli, such as the official Node.js binaries, won't appear in that list and has to be updated separately.
  • Check your distribution, not just your app. Debian has fixed bookworm (DLA-4855-1) and trixie (DSA-6582-1). Ubuntu, at the time of writing, has not. Debian and Ubuntu builds of Node.js use the system Brotli library, so upgrading Node.js alone won't fix them; updating the system library will.
  • Fixed upstream versions are 1.0.10, 1.1.1 and 1.2.1. For Node.js from nodejs.org, nvm or the official Docker images, update to 22.23.4, 24.22.1 or 26.12.1.
  • Restart after updating. A running program keeps the old library in memory until it restarts. Rebuild any container images as well.
  • Removing br from the Accept-Encoding header isn't enough on its own. What matters is whether your HTTP client unpacks responses automatically, not what it says it accepts. A malicious server can send Brotli whether or not you asked for it.

The fix itself was small. The work was knowing our own platform well enough to see that a bug in a compression library could reach a WordPress site through its outgoing requests. Because we run the whole stack, we could find that path, close it the same evening, and tell you exactly what we did.