Node2Shell can turn malformed Brotli data into memory corruption and potential code execution. Fixes span Node.js, .NET, Python packages and Google’s library.

Why Node2Shell matters beyond Node.js
Node2Shell exposes a shared risk across several software stacks: processing attacker-controlled Brotli data can trigger memory corruption and, under the right conditions, remote code execution. The issue crosses language and runtime boundaries because Brotli is a common compression format, while applications may rely on it through runtimes, packages or native libraries. Patches now cover Node.js, .NET, Python packages and Google’s Brotli library, so teams need to check both direct dependencies and runtimes bundled into deployed artifacts.
Tracked as CVE-2026-111497, the high-severity flaw affects applications that use a vulnerable Brotli decoder on untrusted compressed input. The Node.js project disclosed it on October 21, 2026, and Microsoft issued out-of-band .NET updates on October 25. OpenSourceMalware researchers separately identified affected Python packages. Remote code execution has been demonstrated in a public proof-of-concept lab and independently by cybersecurity firm Imperva, which named the issue Node2Shell.
That name can make the vulnerability sound specific to one platform. The affected implementations and patch paths show a broader operational problem: teams can’t assume that updating an application’s source dependencies also updates the code that handles compressed data at runtime. The exposure depends on how a service uses Brotli and where its decoder comes from.
Malformed block transitions corrupt decoder memory
Brotli is a lossless compression format used to shrink data exchanged between applications and servers. Compression lets systems transmit less data, but decoding requires software to interpret a structured stream and reconstruct its contents. Node2Shell lies in how the decoder handles an invalid transition between block types in a compressed stream.
The public proof of concept traces the flaw to DecodeBlockTypeAndLength(). When a stream requests an invalid block switch, the function can return an ambiguous failure value. The decoder’s fast path doesn’t handle that result correctly, allowing execution to continue with invalid internal state and produce an out-of-bounds write. The issue combines an unchecked return value (CWE-252) with an out-of-bounds write (CWE-787).
That chain matters because a decoder is meant to reject malformed input before it can affect memory outside the intended bounds. Here, a failure along the parsing path isn’t handled safely. Memory corruption doesn’t automatically mean an attacker can run code in every deployment; exploitability depends on the surrounding binary and process. But the demonstrated results make this more than a theoretical parsing bug.
The PoC uses a vulnerable Node.js 24.22.0 build for Linux x86-64. Its exploit depends on the binary and memory layout, and may require retries. The same request fails against Node.js 24.22.1, whose patched decoder rejects the invalid transition. OpenAI Codex Security researchers Khai Tran and Sylvie Mayer found the flaw during security testing and reported it to the Node.js project.
The proof of concept is useful evidence of impact, but it doesn’t establish that every affected service can be exploited in the same way. Still, the difference between the vulnerable and patched Node.js builds gives teams a concrete reason to prioritize updates. A decoder’s rejection behavior is the key change.
Patch runtimes, packages and bundled deployments
Node.js fixed the issue in versions 22.23.4, 24.22.1 and 26.12.1, according to its security release. Teams should update to a fixed version or later, rebuild affected container images and redeploy applications that include vulnerable runtimes. End-of-life Node.js versions should be treated as affected and moved to supported releases.
Updating a version on a developer’s machine doesn’t change a container that’s already built or a service that’s still running an older image. The fix has to reach the deployed artifact, which means teams need to connect dependency inventories to their build and release process. If you're working in this space, check the versions inside production images as well as those recorded in package manifests.
Microsoft’s October 25 updates cover .NET 8, 9 and 10, plus .NET 11 Release Candidate 3. Fixed runtime versions are 8.0.33, 9.0.22 and 10.0.14; the preview line is fixed in 11.0.0-rc.3.26522.148. Updated SDKs are available too. Self-contained and Native AOT applications may need to be rebuilt and redeployed because updating a shared runtime won’t replace a decoder already linked into an executable.
That distinction is easy to miss during incident response. A machine can have a patched shared runtime while a self-contained application still carries its own older code. The practical check is whether the deployed application actually uses the fixed decoder, not simply whether an update was installed somewhere in its environment.
Python users should update Brotli to 1.2.1 and brotlicffi to 1.2.1.0, the patched releases identified by OpenSourceMalware. These replace vulnerable versions through 1.2.0 and 1.2.0.2, respectively. Google has also released fixes for its Brotli library in versions 1.0.10, 1.1.1 and 1.2.1.
Package names alone may not settle the question of exposure. Projects can bundle or compile native code, and applications can bring dependencies into a final artifact through more than one path. Teams should trace which implementation is present and whether it processes data an external party can control.
Exposure depends on where decoding happens
Services that decompress untrusted HTTP responses or request bodies face the clearest exposure. A service that only handles trusted, locally generated compressed data presents a different scenario from one that accepts compressed input from outside; the source material doesn’t establish that every use of Brotli is equally reachable. That said, identifying the input path is part of assessing risk, not a reason to skip the patch.
Runtime inventories need to include container images, application bundles and native executables, since a package update alone may not change the decoder that’s actually deployed. At publication, no exploitation in the wild had been publicly reported, though proof-of-concept code had been available for days. The absence of public reports is a limited snapshot, not evidence that an exposed system is safe.
What teams should watch next
This is more significant than it looks because the same class of dependency can sit beneath different languages and deployment models. A team may manage its application code carefully and still miss a vulnerable decoder bundled into a runtime or executable. The patch list spans several ecosystems; the inventory work has to span them too.
Here’s the thing: the most useful response is a direct check of what’s deployed, where it decodes compressed input and whether that decoder is fixed. For systems that rely on shared runtimes, update and redeploy; for self-contained builds, rebuild with patched components. A package manifest is a starting point, not the full answer.
And this is the part most people overlook: compression is often treated as plumbing, so ownership can fall between application and platform teams. Clear responsibility for runtime and artifact updates can make the difference between a patch being available and a service actually running it. No public in-the-wild exploitation had been reported at publication. Still, with working demonstrations available, delay leaves teams relying on conditions they may not have verified.
Not every decoder is equally exposed.
Discussion
Sign in to join the discussion.