Security

Brotli vulnerability CVE-2026-111497: how to check and how we patched

CVE-2026-111497 is a code execution flaw in the Brotli decoder used by Node.js, Python, curl and PHP. How to check your containers, and how BucketDesk patched production in about 40 minutes.

JeVaughn FergusonFounder, developer
October 24, 20267 min read
The short versionPlain termsFor builders

CVE-2026-111497 sits in the Brotli decoder that Node.js, Python, curl and PHP all use, so check libbrotli and Node.js inside every running container today. Install Brotli 1.0.10, 1.1.1 or 1.2.1, or the patched package from your distribution, then rebuild and replace every container.

CVE-2026-111497 is an out-of-bounds write in the Brotli decoder, rated 9.8 in the CVE record. Brotli versions 1.0.3 through 1.2.0 are affected. Crafted compressed data can let an attacker run code in any program that decompresses it. The fix is Brotli 1.0.10, 1.1.1 or 1.2.1, or a runtime that bundles one of them.

It was first reported as Node2Shell, because Node.js bundles the decoder. The same library sits inside Python packages, curl, PHP and most Linux distributions, so the reach is much wider than Node.js. BucketDesk was affected through PHP and curl. This article covers how to check your own servers and what our patch looked like.

What the Brotli vulnerability is

Brotli is a compression format. Browsers and servers use it to shrink web pages and API responses, and the receiving side decompresses the data before reading it.

The bug is in that decompression step. A crafted Brotli stream makes the decoder write past the end of a memory buffer. An attacker who can get such a stream decompressed by your program may be able to run code inside it. A public proof of concept reached code execution only against a deliberately weakened test build. On a standard build, its author reported a crash and a limited memory read.

  • Incoming data: a server that accepts request bodies sent with Content-Encoding: br decompresses them automatically
  • Outgoing requests: an HTTP client that asks for compressed responses decompresses whatever the other server sends back
  • Files: any job that unpacks Brotli files supplied by users

Who is affected by CVE-2026-111497

Every program that links Brotli 1.0.3 to 1.2.0 and decompresses data from outside is affected. The library is often present without anyone choosing it, pulled in as a dependency of curl or an image library.

Vercel added firewall rules on October 21 that block the attack in inbound requests. Those rules do not cover responses your own servers fetch from elsewhere, and they do not replace the update.

  • Node.js: fixed in 22.23.4, 24.22.1 and 26.12.1. Node.js 20 and older are end of life and get no fix
  • Brotli itself: fixed in 1.0.10, 1.1.1 and 1.2.1
  • Python: update the brotli package to 1.2.1, and update brotlicffi where you use it
  • Debian 12 bookworm: libbrotli1 1.0.9-2+deb12u1 from the bookworm-security repository
  • curl and PHP: patched by updating the system libbrotli and restarting every process that loaded it
  • Containers: a running container keeps the old library until the image is rebuilt and the container is replaced

How to check your servers and containers

Check the library version inside every running image, not on the host. A host can be fully patched while its containers still carry an old copy. For Node.js, check the Node.js version itself: 22.23.4 still reports Brotli 1.1.0 in process.versions.brotli, because the fix is backported.

docker run --rm --entrypoint sh <image> -c 'dpkg -l libbrotli1 2>/dev/null | tail -1; node -v 2>/dev/null; php -r "echo curl_version()[\"features\"] & CURL_VERSION_BROTLI ? \"curl decodes br\" : \"\";" 2>/dev/null'
  • Debian or Ubuntu images: libbrotli1 must show the patched package version for your release
  • Node.js: expect 22.23.4, 24.22.1 or 26.12.1 or later
  • PHP: if curl decodes br, PHP is exposed through the system library and is fixed by patching it
  • Rebuild and redeploy: install the fix in the image, rebuild, and replace every container that uses it

How the Brotli flaw reached BucketDesk

An Aikido advisory email arrived saying the flaw went beyond Node.js. We started checking at 02:08 UTC on October 24.

Our application code has no Brotli in it. The production image, built on the official php:8.4-apache image for Debian bookworm, did. It carried libbrotli1 1.0.9, inside the affected range, and the curl extension in PHP was built to decode Brotli responses. Every outgoing HTTPS call our app makes, to payment, email and AWS services, could receive a Brotli response and hand it to the vulnerable decoder. An attacker would need to control or intercept one of those responses to reach it.

Debian had already published the fixed package. Our base image is pinned to an exact version for repeatable builds, so the fix had not reached us on its own. We added one step to install it, confirmed the version in a local build, and shipped it.

Customer files stay in customer buckets throughout. Our server reaches a bucket only through temporary credentials, each set tied to one connection and expiring within 15 to 60 minutes.

  • 02:08 UTC: advisory read, checking starts
  • 02:11 UTC: exposure confirmed in the production image and a fix written
  • 02:35 UTC: fix reviewed and merged
  • About 02:45 UTC: every production container running libbrotli1 1.0.9-2+deb12u1, site healthy

What we changed so the next one is caught on day one

The gap was the pin, not the patch. A pinned base image only changes when someone updates it, and security fixes arrive more often than that. Bugs like this one are now being found faster, many of them by AI tools, so the time from fix to production matters more than ever. We made three changes the same night.

Every production build now applies all Debian security updates. The build checks the Debian security index and reruns the update step only when Debian has published something new.

The runtime image dropped from 208 installed packages to 140. Compilers, development headers and the curl command line tool were only needed to build PHP extensions, so they are now removed before the image ships. Fewer packages means fewer future advisories that apply to us.

A daily scan now checks every running image for high and critical vulnerabilities that already have a fix, and emails us when that list changes. It runs on the production server itself. We pinned the scanner, Trivy, to 0.69.3, the last release before the Trivy supply chain compromise in March 2026. The scanner reads each image from a saved file, so it gets no access to Docker or to AWS credentials.

trivy image --severity HIGH,CRITICAL --ignore-unfixed --scanners vuln <image>
  • Security updates: applied on every build instead of waiting for a new base image
  • Smaller image: 68 packages removed, including the compilers and the curl command
  • Daily scan: fixable high and critical issues in running images reach our inbox the same day
The solution

Limit what a compromised server can reach in S3

A code execution bug becomes a data breach when the server it hits holds long-lived AWS keys. BucketDesk gives your team a web workspace for your own S3 buckets, connected through a role in your account that hands out short-lived credentials scoped to the buckets you choose.

  1. Short-lived credentials

    every request uses STS credentials that expire within 15 to 60 minutes

  2. Scoped connections

    each connection reaches only the bucket, prefix and actions you approve, starting read only

  3. Your audit trail

    each AWS session carries the identity of the BucketDesk user into your CloudTrail

Try it out now

The Minimum plan is free for one bucket.

Primary sources

Discussion

0 comments · open to guests · moderated
Comments appear after a quick review.

Liked this? Get the next article by email. No schedule, no digest, one click to leave.

Keep reading

All articles →