Meet the new Cycode > now you're in command.

Read the blog

Blog

node2shell (CVE-2026-111497): What to patch and how to find exposed services

6 MIN

node2shell (CVE-2026-111497): What to Patch and How to Find Exposed Services

CVE-2026-111497, also called node2shell, is an out-of-bounds write in the Brotli decoder that ships inside Node.js. A crafted Brotli stream can crash the process, leak memory or run code. The fix is Node.js 22.23.4, 24.22.1 or 26.12.1 and Bun 1.4.4, and the services to patch first are the ones that decompress Brotli data they do not control.

What Happened

The bug is in Google’s Brotli C library, which Node.js and Bun bundle. A stream that declares a single block type and then switches blocks was not rejected, so decoding continued in an invalid state and wrote past a buffer (CWE-787, CWE-252). Khai Tran and Sylvie Mayer of OpenAI Codex Security found and reported it to Node.js. Google, the CNA, scored it 9.2 Critical under CVSS 4.0, and Node.js rates it High. The Node.js advisory states that the bug permits remote code execution. Code execution has been demonstrated publicly on the official Node.js 24.22.0 arm64 image, and a public proof of concept exists. As of October 23 no exploitation in the wild has been reported and the CVE is not in the CISA KEV catalog.

Date Event
Oct 22, 2026 CVE published. Code execution demonstrated publicly on Node.js 24.22.0. Bun 1.4.4 released with the fixed Brotli library.
Oct 21, 2026 Node.js releases 22.23.4, 24.22.1 and 26.12.1. Brotli 1.0.10, 1.1.1 and 1.2.1 released.
Feb 27, 2026 Fix committed to the google/brotli repository, with no release or advisory at the time.
Blog – PODCAST CTA – 1Blog – PODCAST CTA – 1Blog – PODCAST CTA – 1

Who Is Exposed

Exposure takes two conditions. The first is a Node.js or Bun runtime below the fixed version, whether it sits in a container base image, on a build host or on a server. The bug is in the runtime, not in an npm package; the npm package named brotli is not the vulnerable component.

The second is a path for attacker data to reach the decoder. The Node.js advisory frames it as Brotli data that an application decompresses with node:zlib: zlib.brotliDecompress, brotliDecompressSync, createBrotliDecompress, or a library that calls them for you. A bare http server does not decode Brotli request bodies.

In Express 5 and body-parser 2.x, json(), urlencoded(), text() and raw() all decode Brotli request bodies. So do the Koa body parsers (koa-bodyparser, @koa/bodyparser, koa-body, co-body) and @fastify/compress with request decompression. The built-in fetch() decodes Brotli responses, so code that fetches user-supplied URLs is also exposed. Express 4 and body-parser 1.x do not decode Brotli request bodies.

Google’s Python Brotli package bundles the same decoder and is fixed in 1.2.1. This post covers Node.js and Bun.

Affected and Fixed Versions

Node.js lists no workaround, so upgrading is the fix. End-of-life Node.js lines get no patch.

Component Affected Fixed in
Node.js 26.x below 26.12.1 26.12.1
Node.js 24.x below 24.22.1 24.22.1
Node.js 22.x below 22.23.4 22.23.4
Node.js 20.x and other end-of-life lines all releases that bundle Brotli none; move to a supported line
Bun below 1.4.4 1.4.4
Google Brotli library 1.0.3 to 1.0.9, 1.1.0, 1.2.0 1.0.10, 1.1.1, 1.2.1
Blog – Demo CTA – 2Blog – Demo CTA – 2Blog – Demo CTA – 2

How Cycode Helps

The Threat Intelligence dashboard has four items for CVE-2026-111497. Each shows how many assets in your organization match and opens the query behind it in the Context Intelligence Graph. Every result links the asset to its repository, so the team that owns the fix is clear.

Services that accept Brotli bodies. This query finds a Dockerfile on a vulnerable Node.js or Bun image and, in the same directory, a dependency that decompresses Brotli request bodies: Express 5, body-parser 2, the Koa body parsers or @fastify/compress. Express 5 and body-parser 2 are the request paths shown to reach code execution in public research, so services that match both conditions are the ones to patch first. A match does not prove the route is reachable from outside, and the query does not see fetch() of user-supplied URLs or direct node:zlib use, so the runtime queries below still matter.

Dockerfiles on vulnerable runtimes. This query finds Dockerfiles that build on Node.js below 22.23.4, 24.22.1 or 26.12.1, on an end-of-life Node.js line or on Bun below 1.4.4. It flags digest-pinned Node.js and Bun images for review, because their version cannot be read from the tag. It also catches nvm install and NodeSource setup lines that install a vulnerable version.

Container images. Two more items look inside built images. One finds images whose Node.js or Bun runtime is below the fixed version. The other finds images that ship the distribution Brotli library in the affected range, for review against your distribution’s advisory, since distributions may backport the fix.

We tested each query against repositories with known-vulnerable and known-fixed versions before release.

First 24 Hours

  1. Find services that run Node.js below 22.23.4, 24.22.1 or 26.12.1, or Bun below 1.4.4, including base images and build hosts.
  2. Start with services that decompress request bodies (Express 5, body-parser 2, Koa body parsers or @fastify/compress), fetch user-supplied URLs or pass untrusted data to the node:zlib Brotli functions.
  3. Upgrade the runtime and rebuild the images, including standalone binaries built with bun build –compile. Replace end-of-life Node.js lines.
  4. Until the rebuild ships, reject Content-Encoding: br at the proxy or load balancer, or turn off inflation in your body parsers. Match br case-insensitively, including inside lists such as gzip, br. Turning off inflation rejects every compressed body, gzip included. Neither covers Brotli responses read by fetch().
  5. Check images for the distribution Brotli library and apply your distribution’s update.
Blog – Demo CTA – 3Blog – Demo CTA – 3Blog – Demo CTA – 3

Sources