Back to Blog
    DevOps & Best PracticesOctober 29, 202611 min read

    CVE-2026-111497: Patch the Brotli Bug on Ubuntu

    By Dana— Server Monitoring & Linux
    Share
    CVE-2026-111497: Patch the Brotli Bug on Ubuntu

    CVE-2026-111497 is a bug in Brotli's decoder, the part of the library that unpacks br-compressed data. Feed it a malformed stream and it writes past the end of a buffer. That can crash the process, leak memory, or in the worst case let an attacker run code. You probably never installed Brotli on purpose. On Ubuntu it comes in as libbrotli1, a dependency of libcurl and a few other common libraries, and it's also compiled straight into Node.js and some other runtimes.

    Ubuntu gives the CVE High priority because CISA has put it on its Known Exploited Vulnerabilities list, and the CVSS v4.0 score is 9.2. Fixed packages are available for every Ubuntu release from 16.04 to 26.10, and installing them takes a couple of minutes. What takes longer is restarting everything that still has the old library loaded, and finding the copies of Brotli that apt doesn't manage. Most of this post is about those two jobs.

    CVE-2026-111497 at a glance

    Component Google Brotli decoder (c/dec/decode.c)
    Bug class Out-of-bounds write (buffer overflow) during decompression
    Affected upstream versions Brotli 1.0.3 through 1.2.0
    Fixed upstream versions 1.0.10, 1.1.1 and 1.2.1
    Attack Remote, no authentication. The target only has to decompress Brotli data the attacker supplies.
    Impact Crash, memory disclosure, possible remote code execution
    CVSS v4.0 9.2 Critical (AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H)
    Ubuntu priority High (listed in CISA KEV)
    Ubuntu notices USN-8956-1 (22.04 to 26.10), USN-8956-2 (16.04 to 20.04)
    Also known as Node2Shell, HTTP2Shell, Brotli2Shell, Broccoli2Shell

    How the bug works

    A Brotli stream is made of blocks, and the decoder switches between block types as it reads. The Node.js security advisory explains what went wrong. When the decoder hit an invalid block switch, it treated it as "not enough input yet" instead of a fatal format error. So it kept waiting for more data, and each new chunk was appended to an internal buffer that is only eight bytes long. Everything after those eight bytes lands in memory that belongs to something else.

    The fix itself isn't new. It went into the google/brotli repository in February 2026, but there was no release and no advisory until this month, so the patch sat in public view for about eight months without anyone shipping it. Node.js disclosed the bug on 21 October. That's why most of the nicknames point at Node, even though the problem lives in Brotli.

    Things have moved fast since then. Debian and Ubuntu published CVE entries on 22 and 23 October, and Python's Brotli packages were confirmed affected around the same time. Ubuntu released USN-8956-1 for 22.04 and newer on 26 October, the same day the first exploitation attempt in the wild was reported. USN-8956-2 followed on 27 October for 16.04, 18.04 and 20.04, and CISA added the CVE to KEV with a 10 November deadline for US federal agencies.

    Proof-of-concept code is already on GitHub. From what has been published so far, a working exploit has to be tuned to one specific build, and most attempts crash the target instead of giving the attacker a shell. That shouldn't make you relax. A crash is still an outage, and when systemd or PM2 restarts the worker, the attacker just gets another try.

    Who is exposed on an Ubuntu server

    Only decompression is affected, so turning off Brotli compression on your web server or CDN won't help. The question to ask is whether anything on the box decompresses Brotli data that someone else controls. That can happen in both directions.

    On the inbound side, look at services that accept request bodies sent with Content-Encoding: br and inflate them: API gateways, Node.js or Python apps whose framework decompresses request bodies for you, and proxies like Envoy or Istio with a Brotli decompression filter.

    On the outbound side, it's anything that fetches a URL and decodes a br response automatically. Node's fetch() and node:zlib do this. So do Python's requests, urllib3, httpx and aiohttp when the Brotli package is installed, and so does curl with --compressed. In practice that means link previews, webhook senders, scrapers, feed readers and most third-party API integrations. Dropping br from your Accept-Encoding header doesn't really help, because a malicious server can send Brotli whether you asked for it or not.

    The other catch is that Ubuntu's update only fixes Ubuntu's copy of the library:

    Where Brotli lives Fixed by the Ubuntu update? What fixes it
    libbrotli1, brotli, python3-brotli from the Ubuntu archive Yes The package versions below, plus a restart
    Ubuntu's own nodejs package on 22.04 and later Yes, it links the system library The libbrotli1 update, then restarting every Node process
    Ubuntu's nodejs package on 20.04 No. It bundles its own Brotli and Ubuntu lists it as vulnerable Switching to a supported upstream Node.js build
    Node.js from NodeSource, nvm, official tarballs or node: Docker images No Node.js 22.23.4, 24.22.1, 26.12.1 or later (end-of-life lines like 18 and 20 get no fix)
    Python Brotli / brotlicffi installed with pip, in virtualenvs or images No Brotli 1.2.1 or later, brotlicffi 1.2.1.0 or later
    Bun, .NET, Envoy, Istio No Bun 1.4.4+, .NET 8.0.33 / 9.0.22 / 10.0.14, or your vendor's current patch release

    On 18.04 and older, Ubuntu's nodejs package doesn't use Brotli at all, so there's nothing to do there.

    Fixed Ubuntu package versions

    The same version applies to brotli, libbrotli1 and python3-brotli, plus python-brotli on 18.04 and 16.04:

    Ubuntu release Fixed version Notice
    26.10 (Stonking) 1.2.0-4ubuntu0.1 USN-8956-1
    26.04 LTS (Resolute) 1.2.0-3ubuntu0.1 USN-8956-1
    24.04 LTS (Noble) 1.1.0-2ubuntu0.1 USN-8956-1
    22.04 LTS (Jammy) 1.0.9-2ubuntu0.1 USN-8956-1
    20.04 LTS (Focal) 1.0.7-6ubuntu0.1+esm1 (Ubuntu Pro) USN-8956-2
    18.04 LTS (Bionic) 1.0.3-1ubuntu1.3+esm1 (Ubuntu Pro) USN-8956-2
    16.04 LTS (Xenial) 1.0.3-1ubuntu1~16.04.2+esm1 (Ubuntu Pro, Legacy Support add-on) USN-8956-2

    These numbers look older than upstream's fixed releases (1.0.10, 1.1.1 and 1.2.1), and that's expected. Ubuntu backports the fix into whatever version each release already ships, so 1.1.0-2ubuntu0.1 on Noble is patched even though "1.1.0" looks behind. Go by this table, not by the upstream version number.

    On 20.04 and older, the update only reaches machines attached to Ubuntu Pro. Pro is free for personal use on up to five machines.

    How to patch Ubuntu for CVE-2026-111497

    1. Check what's installed

    apt-cache policy libbrotli1                 # "Installed" should be at least the fixed version above
    dpkg -l | grep -i brotli                    # every Brotli package on the host
    apt-cache rdepends --installed libbrotli1   # what on this box depends on it
    

    2. Upgrade the packages

    sudo apt update
    sudo apt install --only-upgrade libbrotli1 brotli python3-brotli
    

    A normal sudo apt upgrade works too and picks up everything else that's pending. On 20.04, 18.04 or 16.04, attach the machine to Ubuntu Pro first and let the Pro client handle it:

    sudo pro status
    sudo pro fix CVE-2026-111497
    

    3. Restart whatever loaded the old library

    Upgrading replaces the file on disk, but a process that's already running keeps the old libbrotlidec in memory until it restarts. This is the step people skip, and it's how a server can show as patched and still be vulnerable weeks later. This lists every process still using the old file:

    sudo grep -l 'libbrotli.*(deleted)' /proc/[0-9]*/maps 2>/dev/null \
      | cut -d/ -f3 | paste -sd, | xargs -r ps -o pid=,comm= -p
    

    If it prints nothing, you're done. Otherwise restart those services, or reboot if there are a lot of them. Ubuntu Server installs needrestart by default from 22.04 on, and sudo needrestart -r l shows the same list grouped by service.

    4. Find the copies apt doesn't manage

    which -a node && node -v                    # want 22.23.4, 24.22.1, 26.12.1 or later
    python3 -m pip list 2>/dev/null | grep -iE '^brotli(cffi)? '
    sudo find / -xdev \( -name 'libbrotli*.so*' -o -name '_brotli*.so' \) \
      -not -path '/usr/lib/*' 2>/dev/null       # private copies in venvs, /opt, app bundles
    

    Run the pip check inside each virtualenv, not only against the system Python. If you use nvm, check every Node version you have installed. Node compiles Brotli into the node binary itself, so find won't turn it up. The version number is the only check that works there.

    5. Rebuild your containers

    Patching the host doesn't touch your containers. Every image has its own libbrotli1, its own Node binary and its own pip packages. Rebuild from an updated base image (run apt-get upgrade in the build or move to a newer node: tag), redeploy, and make sure the old containers have actually stopped.

    If you can't patch right away

    Upgrading and restarting is the only real fix. If a service has to stay on the old code for a few more hours, you can at least reduce the exposure:

    • At the reverse proxy, reject Brotli-encoded request bodies for apps that don't need them. In nginx:

      # server block of the proxy in front of the app
      if ($http_content_encoding ~* "br") {
          return 415;
      }
      
    • Remove Brotli and brotlicffi from Python environments until you can upgrade them. Without the package, urllib3, requests, httpx and aiohttp can't decode br at all.

    • In Envoy and Istio, turn off the Brotli decompressor filter in both directions.

    • Pause features that fetch URLs on a user's behalf, like link previews or webhook tests, or move them to an isolated worker. They're the easiest way to get your server to download a hostile response.

    Monitoring during the patch window

    Monitoring won't block a malicious Brotli stream, and a quiet dashboard doesn't mean nobody tried. It's still useful this week in a few specific ways.

    Know where Brotli runs

    Start your patch list from the servers that already report to your monitoring agent, then look for the ones that don't. There's usually a staging box or an old worker VM that isn't on anyone's list.

    Watch for crashes

    Since most exploit attempts crash the process, failed attacks leave traces. The catch is that systemd, PM2 and Docker restart crashed workers automatically, so if you only check whether a service is up, you won't see them. Look for:

    • segfaults in the kernel log, particularly in node, python3 or libbrotlidec:

      journalctl -k --since "-7 days" | grep -iE 'segfault|general protection'
      
    • restart counts going up: systemctl show -p NRestarts <service>, the restart column in pm2 list, or your container restart counts

    • memory spikes or OOM kills on services that normally use very little memory

    • processes that shouldn't be there, such as shells, curl or wget started by node or python3, and new outbound connections from app servers

    Confirm the restarts worked

    Once everything is restarted, check that it all came back: no containers stuck in a restart loop, no worker pools sitting at zero, every public endpoint responding. This is also how you know the new library is the one actually running.

    How Xitoring helps

    If you already use Xitoring, these are the parts that help with the steps above:

    What you need Where to find it
    Memory, CPU and process metrics on every Ubuntu host Linux server monitoring with the Xitogent agent
    Spotting containers that crash-loop, restart or get OOM-killed Docker monitoring, which tracks restart counts and OOM kills per container
    Keeping an eye on your proxies while you add the br block and restart backends Nginx and Apache monitoring
    Knowing every site came back after the restarts Uptime monitoring with HTTP checks from several regions

    PHP2Shell was disclosed on the same day as this bug. If you run PHP and Node.js on the same servers, you can deal with both in one round of upgrades and restarts.

    Checklist

    1. Upgrade libbrotli1, brotli and python3-brotli to the fixed version for your release. On 20.04 and older, that means Ubuntu Pro.
    2. Restart every process that still maps the old library, or reboot.
    3. Update the copies apt can't see: Node.js to 22.23.4, 24.22.1 or 26.12.1, pip Brotli to 1.2.1 and brotlicffi to 1.2.1.0. Then rebuild your container images.
    4. Keep watching for segfaults, restart loops and unexpected processes on anything that decompresses data from outside.

    Migrate in one click.

    Switching from Pingdom, Uptime.com, or BetterStack? Import all your monitors instantly.

    Start Migration