One Brotli Decoder, Every Windows Build, and Which Systems Are Actually at Risk

CVE-2026-111497 touches every Windows version, but 'every system that handles web traffic' quietly overstates your exposure. Here's how to sort your fleet by actual decompression risk and patch in the right order.

One Brotli Decoder, Every Windows Build, and Which Systems Are Actually at Risk

A compression bug that reaches “every Windows version” is the kind of line that gets a change window approved before lunch. It’s also the kind of line that, read literally, sends people patching the wrong boxes in the wrong order. So before anyone drains the on-call coffee: the claim worth pressure-testing isn’t is there a bug, Microsoft published the advisory, the fix is in the November 2026 cumulative updates, go install it. The claim worth testing is the scary one in the headline: this touches every Windows system that handles web traffic.

Partly true. Partly the sort of thing that makes a flaw sound bigger than your actual attack surface. Let’s take it apart.

Does it really hit every Windows version?

Yes, and that part isn’t spin. Brotli decompression lives in a shared piece of the OS, not a one-off in some optional feature nobody installs. When a decoder sits that low in the stack, the list of “affected products” in the MSRC entry reads like a Windows release history: client and server, current builds and the LTSC ones you forgot were still racked in a closet.

One practical trap before you start: Microsoft ships a different KB per build. There is no single “the patch” number to grep for. The November cumulative update for Server 2022 is a different KB than Windows 11 24H2, which is different again from Server 2019. If you built an audit around one KB string you pulled off a forum, you’ll get clean-looking reports and dirty-looking servers. Pull your build’s specific KB from the MSRC entry, not from memory.

Who actually decompresses attacker-controlled Brotli?

This is where “every system that handles web traffic” quietly overstates things, and where you can save yourself a weekend.

Decompression happens on the side that receives compressed bytes. On the web, that’s almost always the client. Your browser sends Accept-Encoding: br, the server replies with Content-Encoding: br, and the client expands it. So the classic exposure here is a machine fetching untrusted content: a workstation browsing out, a build agent pulling packages, a server-side process calling third-party APIs over WinHTTP or WinINET with Brotli enabled.

The average IIS box sitting behind a load balancer is usually doing the opposite job, compressing responses on the way out, not decompressing hostile input on the way in. It only enters the blast radius if it decompresses inbound: request bodies with Content-Encoding: br, upstream fetches, a reverse-proxy or API gateway role that expands Brotli from somewhere it doesn’t fully trust. Plenty of servers do exactly that. Plenty don’t. The difference matters more than the OS version.

And the browsers deserve a footnote, because they’re the first thing people panic about. Edge and Chrome ship their own statically-linked Brotli. A Windows cumulative update doesn’t touch the copy inside the browser, those get fixed on the Chromium release train. So “patch Windows and the browser’s covered” is wrong in both directions: the OS fix won’t close a browser’s bundled decoder, and the browser’s update won’t close the OS component your line-of-business app links against. Two patch lines, two owners. Track both.

What’s the mechanism, briefly?

Skip the Brotli internals; the shape is familiar. A decoder reads length and offset fields out of the compressed stream and uses them to write into an output buffer. Feed it a stream whose declared sizes disagree with what it actually allocated, and the write runs off the end of the buffer. Heap corruption. From there it’s the usual ladder, best case a crash and a denial of service on whatever process was unlucky enough to be decoding, worse case a crafted payload that grooms the heap into something that executes. Microsoft’s write-up carries the remote-code-execution rating, so treat the ceiling as RCE even if most real-world attempts land as a crash loop.

The ugly detail is trigger simplicity. The attacker doesn’t need a session, a login, or a second bug to chain. They need your code to decompress a blob they control. A malicious response to a request your software made is enough.

No confirmed in-the-wild exploitation at the time of writing, MSRC hasn’t flagged it, and I haven’t seen credible threat-intel claiming a working exploit. That’s a reason to move deliberately, not a reason to go back to sleep. The ubiquity does the arguing here; nobody needs to manufacture urgency on top of it.

How do I find what’s actually behind?

Resist the Get-HotFix reflex. On modern Windows, cumulative updates supersede each other, so a given KB often won’t appear by name even when the fix is installed, it got rolled into a later rollup. Audit the build revision (UBR) instead and compare against the November level, or let your management plane do it. This checks locally and reports only, set the patch-release date to match your build’s actual release before you trust the verdict:

audit.ps1PowerShell
# Report-only. Flags machines whose last cumulative update predates November Patch Tuesday.
# Set $PatchRelease to the release date for YOUR build from the MSRC entry.
$PatchRelease = [datetime]'2026-11-10'
​
$os  = Get-CimInstance Win32_OperatingSystem
$ubr = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').UBR
$lastCU = Get-CimInstance Win32_QuickFixEngineering |
    Where-Object { $_.Description -match 'Security Update|Update' -and $_.InstalledOn } |
    Sort-Object InstalledOn -Descending | Select-Object -First 1
​
[pscustomobject]@{
    Host        = $env:COMPUTERNAME
    OS          = $os.Caption
    Build       = "$($os.Version).$ubr"
    LastUpdate  = $lastCU.HotFixID
    InstalledOn = $lastCU.InstalledOn
    Status      = if ($lastCU.InstalledOn -ge $PatchRelease) { 'LIKELY CURRENT' } else { 'REVIEW - predates Nov update' }
}

For anything above a handful of hosts, drive it from the fleet. If you run Defender for Endpoint, its threat-and-vulnerability data already maps the CVE to affected machines, ask it directly instead of polling every box:

audit.ps1PowerShell
# Defender for Endpoint TVM API - lists machines still exposed to the CVE.
# $token = app-only bearer for api.securitycenter.microsoft.com
$headers = @{ Authorization = "Bearer $token" }
$cve = 'CVE-2026-111497'
$uri = "https://api.securitycenter.microsoft.com/api/vulnerabilities/$cve/machineReferences"
​
(Invoke-RestMethod -Uri $uri -Headers $headers -Method Get).value |
    Select-Object computerDnsName, osPlatform, rbacGroupName |
    Sort-Object computerDnsName |
    Format-Table -AutoSize
# Cross-check against Intune device compliance via Microsoft.Graph (Get-MgDeviceManagementManagedDevice)
# to catch machines Defender doesn't see.

The gap between those two queries, endpoints Defender knows about versus the ones only Intune sees, versus the ones in neither, is usually where the real risk hides. The forgotten jump box always decompresses something.

What goes first, and what can wait?

Order by data flow, not by hostname.

  • Immediate: anything that decompresses inbound untrusted Brotli, internet-facing reverse proxies, API gateways, and app servers that accept Content-Encoding: br on request bodies. Plus internet-exposed services making server-side fetches of third-party content.
  • This week: workstations and internal HTTP clients. They’re exposed through ordinary browsing and API calls, but the path to them usually needs a user to go somewhere or a service to call out. Real risk, slightly longer fuse.
  • Next cycle: isolated boxes with no untrusted compressed input and no outbound web path. Patch them on the normal train, just don’t let “next cycle” quietly mean “never” for that LTSC server nobody owns.

And if the change window won’t open until next week?

You can shrink the surface at the edge, as long as you’re honest about which direction you’re protecting.

On a reverse proxy or WAF in front of servers that decompress inbound requests, strip Content-Encoding: br from incoming requests (or reject them) so your Windows boxes never expand hostile bodies. To protect downstream clients, strip br from the Accept-Encoding header on the way out so upstreams fall back to gzip, a tolerable performance hit for a few days. In nginx terms that’s roughly:

run.shbash, zsh
# Force Brotli off between proxy and backend/clients as a stopgap.
proxy_set_header Accept-Encoding "gzip, deflate";   # outbound: no br from upstream
# Reject br-encoded request bodies hitting a decompressing backend:
if ($http_content_encoding ~* "br") { return 415; }

The limit is obvious: this helps where traffic actually passes through your proxy. It does nothing for a laptop on a coffee-shop network pulling Brotli straight off the internet. For endpoints, the compensating control is the patch, and there isn’t a clever header trick that changes that.

The verdict, sharper than the headline

“Hits every Windows version” is accurate. “Touches every system that handles web traffic” is the part that needs an asterisk: the OS component is everywhere, but the systems that can actually be driven by this are the ones decompressing untrusted Brotli, clients fetching outward and servers that expand inbound. That’s a large set, not a universal one, and sorting your fleet by that distinction is the difference between a clean weekend and a frantic one.

Patch the OS. Patch the browsers separately, because Microsoft didn’t do it for you. And remember the thing the advisory won’t spell out: the box that gets you is never the one on the dashboard, it’s the unmanaged relay in the corner, quietly decompressing whatever the internet hands it.