Your Brotli2Shell patch ticket will reassure you, right up until you find out the old copy is still the one running.
Imagine your car maker sends a recall letter: faulty part, new design available now. Very kind of them. Except the letter doesn’t fix anything, and the old part is still bolted into every car already on the road, including the one on your drive.
That’s Brotli2Shell (CVE-2026-111497), or Broccoli2Shell if you like your vulnerabilities with a side of veg. Google fixed a memory bug in its Brotli decompression library on 27 February, as a normal bug fix. Nobody knew it was a security hole until researchers from OpenAI’s Codex Security team flagged it, Node.js disclosed it on 21 October, and six days later CISA added it to its list of known exploited vulnerabilities. It took eight months to go from fix to emergency! (It also went “slightly under the radar”, as The Register put it, because PHP2Shell landed the same week. I wrote about that one in Early bird gets the shell.)
Why isn’t one patch enough?
Because Brotli isn’t something you installed. It’s built into the things you installed. Tenable’s list covers Node.js, .NET, Bun, Envoy, Istio and Python HTTP clients. And Wiz points out that some HTTP clients and frameworks decompress Brotli automatically, “even when Brotli is not explicitly referenced in application code.” So your team can honestly say “we don’t use Brotli” and still be running it on every request.
Of course, your OS update does fix something. Programs that load the system copy of Brotli pick up the fix once they restart. But Node.js and Bun carry their own copy, and keep the flawed one “until a fixed release of that application is installed” (Tenable again). Containers are worse, because the old copy is baked into the image and comes straight back on the next deploy until you rebuild it.
So “we patched Brotli” is really three jobs: the OS, every runtime with its own copy, and every image built on top of either. Most tickets only cover the first.
Who invited Brotli?
Nobody, and that’s the point. Your team picked Express, or ASP.NET Core, or a Python HTTP client. That pulled in a runtime, and the runtime pulled in Brotli, a library nobody on your team has ever chosen (or, let’s be honest, heard of). It’s a dependency of a dependency, sitting at the bottom of the well that the whole internet drinks from.
Remember, attackers understand your supply chain better than you do. Why break into a thousand companies one at a time when you can find one flaw in a library they all share? Touch the one library nobody looks at, and the poison flows up into every runtime, every app and every image built on top of it. The supply chain is the attack surface.
Of course, nobody poisoned Brotli on purpose. This was an honest bug, fixed as a normal bug fix. But we’ve already seen the deliberate version. In March 2024, Andres Freund, an engineer at Microsoft, noticed SSH logins on his machine eating too much CPU and dug into it. He found a backdoor in xz Utils, another compression library nobody chooses directly. A maintainer going by “Jia Tan” had spent years earning trust on the project before slipping it into versions 5.6.0 and 5.6.1. It reached SSH servers on some Linux distros through a chain nobody would draw on a whiteboard: sshd loads libsystemd, which loads liblzma, which is xz. It was caught because one person got curious about half a second of lag. That’s not a security strategy, that’s luck!
Honest bug or planted backdoor, the question on the day is the same: where is every copy? Your package.json lists what you asked for. The well is everything that came with it.
Why is my scanner lying to me?
It isn’t lying. It’s just wrong in both directions at once (impressive, really).
It shows you too many hits. Microsoft says a .NET app “is not exposed merely because an affected .NET package is installed.” What matters is whether the app actually decompresses Brotli data from outside: request decompression, HTTP clients unpacking responses, or code calling the Brotli APIs directly.
It also shows you too few. A copy compiled into a runtime or a base image doesn’t always show up as “brotli” at all.
So the list you need isn’t “where is the package”. It’s “where is the code path, can the internet reach it, and is the fixed one running?” Those are three different questions, and a spreadsheet export answers none of them.
How long have I got?
It’s less than your policy thinks! US federal agencies have until 10 November, two weeks after the KEV listing. FIRST’s EPSS model rates it more likely to be exploited than 99.6% of scored CVEs. And ELLIO says it saw an exploitation attempt against Node.js on 26 October.
Now think about who found this one. I don’t think it’s a coincidence that an AI security team spotted what everyone else had shipped as a routine fix eight months earlier. Finding flaws is getting faster, so fixing them has to as well. The 30-day window was already out of date for PHP2Shell, and it hasn’t got any younger since.
What do I do this week?
-
Find the runtimes. Node.js below 22.23.4, 24.22.1 or 26.12.1. .NET below 8.0.33, 9.0.22 or 10.0.14. Python
brotlibelow 1.2.1 orbrotlicffibelow 1.2.1.0. Check Envoy, Istio and Bun against Tenable’s table. Remember, Node.js 20, 23 and 25 are out of support and get no fix at all, so those are upgrades, not patches. -
Search your code, not just your servers. This catches Node base images, pinned Node versions and the Python and Go packages:
grep -rniE "brotli|FROM node:" --include="Dockerfile*" --include="*requirements*.txt" --include="go.mod" --include=".nvmrc" . -
Rank by reachability. Internet-facing services that accept
Content-Encoding: brgo first, then anything that fetches from servers you don’t control. -
Rebuild, redeploy, restart, then check what’s actually running. Wiz’s advice is to “verify that patched components are loaded”. Run
kubectl exec <pod> -- node -vrather than trusting the image tag. Check versions, not vibes. -
Mitigate what has to wait. Block requests marked
Content-Encoding: brat the proxy or load balancer. Tenable’s caveat: this does nothing for apps that fetch Brotli responses from a hostile server.
Step one decides how long everything else takes. The simple maths: every day your engineers spend building the list by hand is a day of salary spent on a spreadsheet, while the old copy keeps answering requests.
Does triage really take that long?
It doesn’t have to. On one AWS development and QA account at a Nordic digital marketing firm, our engine took around 10,000 security findings and turned them into a ranked set of remediation proposals in a single run, with no added headcount. The scanner’s list is where the work starts. Ranking it by real exposure is the work.
Anyway, don’t be like me and close the ticket because the image tag looked right. Here at Firemind we’ve built the IT Operations Engine to keep a current map of what runs across your cloud and your code, raise dependency and image fixes as pull requests your engineers review, patch inside your rules, and hold higher-risk changes for a human, with every step recorded. Scope a free pilot on one account and one repository, and find out where your Brotli lives before someone else does.
Sources
- Tenable Research, Brotli2Shell FAQ, Satnam Narang, 26 October 2026 (updated 27 and 28 October): upstream fix on 27 February; Node.js disclosure on 21 October; CVSS scores; affected software and fixed versions; Node.js 20, 23 and 25 get no fix; OS copy versus built-in copy; Microsoft’s exposure guidance; ELLIO’s exploitation attempt on 26 October; the
Content-Encoding: brmitigation and its caveat. - Wiz Research, Merav Bar, 25 October 2026: automatic decompression in HTTP clients and frameworks; rebuild, redeploy, restart and verify.
- The Register, Connor Jones, 26 October 2026: Microsoft’s out-of-band .NET fixes on 25 October; discovery credited to OpenAI’s Codex Security; the nicknames; the PHP2Shell timing.
- LWN, “A backdoor in xz”, March 2024: Andres Freund’s discovery of the backdoor in xz Utils 5.6.0 and 5.6.1 (CVE-2024-3094), introduced by co-maintainer “Jia Tan”, and how it reached sshd through libsystemd and liblzma on some distributions.
- CISA Known Exploited Vulnerabilities catalogue, v2026.10.30: CVE-2026-111497 added 27 October, due 10 November (US federal agencies).
- FIRST EPSS, 29 October 2026: score 0.82641, percentile 0.99665.
- Firemind case study: around 10,000 findings triaged into ranked remediation proposals in a single engine run, on one AWS development and QA account, with no added headcount.



