← Verdict cards Cutsign
Patch lane No exploitation in the wild PoC published Not in KEV as of 2026-10-23

Node.js October security releases

10 CVEs fixed October 21, 2026 — 4 high, 3 medium, 3 low — across all supported lines. Headline flaw: Brotli decoder out-of-bounds write, CVE-2026-111497, demonstrated as code execution ("Node2Shell," Imperva, Oct 23). Node.js rates it High; the CVE record (Google as CNA) scores it CVSS 4.0 9.2 Critical; Tenable and Singapore's CSA list 9.8 under v3.1.

On Cutsign: brief story 10-22 Node2Shell update 10-23 CISO card news & updates (live) IoC card: none — no indicators published

Verdict: patch this cycle, not an incident. No in-the-wild exploitation reported and no published attack behaviors to hunt — this is a patch-inventory problem, including every container image and single-executable binary that bundles its own copy of Node. Node.js published no workarounds for any of the ten flaws. Escalate to incident handling only on the triggers below.

30-second read

Feeding crafted Brotli-compressed data to Node's bundled decoder corrupts memory. Imperva reported attacker-supplied code execution on an official Node 24.22.0 Linux arm64 build; the circulating public PoC reaches full code execution only on a deliberately weakened build — on standard builds it crashes the process and leaks a limited slice of memory. The reachable paths are Node's built-in fetch() decoding a Brotli response from a server the attacker controls, and Express 5 body parsers decoding Brotli request bodies. The same release also fixes TLS certificate-check bypasses via unusual IP-address formats and a startup-snapshot flaw that makes randomUUID()/randomInt() repeat values in snapshot/SEA builds.

Exposed if

  • App fetches and decodes untrusted Brotli-compressed responses via built-in fetch()
  • Express 5.0.0–5.3.0 / body-parser 2.0.0–2.3.0 parse request bodies on routes accepting Brotli encoding
  • Node 20.x or earlier anywhere — end-of-life, no fix exists

Not exposed if

  • Plain Node HTTP server that never decodes Brotli request bodies
  • No decoding of untrusted Brotli data at all
  • Outbound fetches pin Accept-Encoding: gzip, deflate (interim mitigation — removes the response-side path, not a substitute for patching)

Caution: a platform WAF in front of inbound requests does not cover the outbound fetch() path.

Fixed versions — October 21, 2026

LineUpgrade toNote
26.x (current)26.12.1all ten fixes
24.x (LTS)24.22.1rejects the Node2Shell PoC input
22.x (LTS)22.23.4oldest fixed line
20.x and earlierno fixEOL April 30, 2026 — migrate

The ten, briefly

CVESevWhat
CVE-2026-111497HighBrotli decoder OOB write via node:zlib — invalid block switch treated as "need more input", appending into an 8-byte buffer; crash, memory disclosure or RCE (Node2Shell)
CVE-2026-105483Hightls.checkServerIdentity misses fullwidth-digit IPv4 (e.g. 127.0.0.1) — a cert for DNS:127.0.0.1 is accepted. 26.x/24.x only
CVE-2026-106069HighLeading-zero IPv4 (0127.0.0.1) read as octal for cert checks but decimal by some OS resolvers — authenticates a different IP than connected
CVE-2026-105482Highdns.resolve*/reverse truncate at a NUL — attacker.example\0.trusted.example resolves as the attacker's name
CVE-2026-106072MedNon-canonical IPv4 spellings (0x7f.0.0.1, 127.1, decimal int) let a DNS-only SAN authenticate an IP endpoint
CVE-2026-106074MedEntropy cache saved into --build-snapshot snapshots / SEA binaries — randomUUID/randomInt repeat values across every process; rebuild those artifacts on a patched runtime
CVE-2026-105481MedKeep-alive sockets accept responses written before the request is sent
CVE-2026-106070Lowzlib.ZipFile returns attacker-controlled bytes after header validation fails (26.x only)
CVE-2026-106071LowPermission Model wildcard over-grant: --allow-fs-read=/etc/passwd.* also grants /etc/passwd (26.x only)
CVE-2026-106073Lowfs.realpathSync skips read-permission checks under --permission — path enumeration outside permitted dirs
Verified against the official nodejs.org advisory (secondary coverage printed mismatched IDs for this release). Release also refreshes bundled OpenSSL, Undici, SQLite, c-ares and the CA store (drops compromised eMudhra certificates).

Detection coverage — as of Oct 23

CategoryVendorStatus
VendorNode.js projectFixed releases Oct 21 (22.23.4 / 24.22.1 / 26.12.1) — no workarounds published for any of the ten; EOL lines get nothing (commercial support only via OpenJS Ecosystem Sustainability partners); snapshot/SEA artifacts need rebuilding on a patched runtime; official per-host checker is-my-node-vulnerable
VM scannersQualysQID 389117 — covers CVE-2026-105481 / -105482 / -106069 / -106072 / -106073 / -106074 / -111497; flags builds below 22.23.4 / 24.22.1 / 26.12.1
TenableNo Nessus plugin yet for the October release — newest Node.js plugin still covers July 2026. CVE page is live (9.8 CVSS v3.1)
Rapid7Nothing published — check InsightVM content release notes
EDR / endpoint VMCrowdStrikeNothing published on Falcon/Spotlight coverage — verify in the console or with support
Microsoft Defender VMNothing published
SentinelOneNothing published
WAF / edgeAkamaiNo published rule for App & API Protector; Cloudflare and AWS WAF managed-rule changelogs also silent as of Oct 22. Vercel shipped emergency WAF rules
All WAFsInbound request bodies only — never the outbound fetch() path; a rule is a stopgap, not a patch
Cloud / containerWiz / Orca / Trivy / GrypeNothing published; image scanners flag this only if they map the bundled Node binary or the system brotli package — verify with a known-bad image before trusting registry scans
Linux distrosErrata shipped — the CVE targets the Brotli library itself (1.0.3–1.2.0), so RHEL and AlmaLinux published brotli updates (RHSA-2026:2228, ALSA-2026:2042 / -2389); OS package scans catch distro copies, not official tarballs or nvm installs
CommunityNuclei / OSSNo template found; the Node.js project ships npx is-my-node-vulnerable for per-host checks

Applies across every scanner above: this is a runtime flaw — SCA/dependency scanners will not surface it, and host scans see installed Node, not copies baked into container images or SEA binaries.

Do now

  • Inventory every Node runtime: hosts, container images, SEA binaries — each image bundles its own copy and needs a rebuild, not just a host patch
  • Upgrade to 26.12.1 / 24.22.1 / 22.23.4; rebuild and redeploy images
  • Quick per-host check: npx is-my-node-vulnerable (official Node.js project tool)
  • Flag anything on EOL 20.x or earlier for migration — it stays vulnerable forever
  • Where patching lags: pin outbound Accept-Encoding away from br and audit routes accepting Brotli bodies

Escalation triggers

  • In-the-wild exploitation reported for CVE-2026-111497
  • CISA KEV listing for any of the ten
  • Weaponized exploit published beyond the Imperva PoC
  • Hunting note: sources print no attack commands or detection queries yet — until they do, effort goes to patch inventory, not log hunts

Inferred detections — while you wait

No vendor-published queries exist for this flaw. The logic below is inferred from the exploit shape (crafted Brotli → memory corruption → crash or code execution), not published by anyone — tune to your environment and expect noise. Failed exploitation is loud; success may be quiet.

Node crash canary — on standard builds the public PoC crashes the process, so a burst of Node crashes is the cheapest tripwire:


# EDR / syslog / crash telemetry
process.name = node (or your bundled runtime names)
termination.signal IN (SIGSEGV, SIGABRT, SIGBUS)
→ alert when count per host > baseline over 1h; triage what it was fetching

Node spawning shells — generic post-exploitation shape for any runtime RCE:


# EDR process lineage
parent.name IN (node, nodejs)
child.name IN (sh, bash, dash, curl, wget, python*, perl)
exclude: build/CI hosts where this is routine

Response-side exposure — the riskier path is your servers fetching Brotli from outside; proxy logs can show which services actually do:


# egress proxy / NetFlow with HTTP metadata
src IN (server subnets) AND response.content_encoding = br
AND dest.domain not seen before (e.g. first_seen < 7d)
→ inventory which apps decode untrusted Brotli at all; those patch first

Splunk (index=*) / Elastic ES|QL (FROM logs-*) — CIM Endpoint / Web field names for Splunk, ECS for Elastic, Sysmon names for lineage; tune to your add-on or integration:


# node crash canary: more than a baseline of crashes per host per hour
index=* earliest=-30d@d (process_name="node" OR Image="*/node" OR "node[") (signal IN ("SIGSEGV","SIGABRT","SIGBUS") OR "SIGSEGV" OR "SIGABRT" OR "SIGBUS")
| bin _time span=1h
| stats count by _time host
| where count>3

# node spawning shells (exclude build/CI hosts where this is routine)

index=* earliest=-30d@d ParentImage IN ("*/node","*/nodejs") Image IN ("*/sh","*/bash","*/dash","*/curl","*/wget","*/python*","*/perl")
| where NOT cidrmatch("<build/CI subnets>", src_ip)
| stats count values(CommandLine) as cmds by host ParentImage Image

# response-side exposure: servers fetching Brotli from new destinations

index=* earliest=-7d@d (response_content_encoding="br" OR "content-encoding: br")
| where cidrmatch("<server subnets>", src)
| stats min(_time) as first_seen count by src dest
| where first_seen > relative_time(now(), "-7d@d")

# version inventory (software-inventory or process events carrying a version string)

index=* earliest=-7d@d (process_name="node" OR "node.exe") version=*
| eval fixed=case(match(version,"^26\."), "26.12.1", match(version,"^24\."), "24.22.1", match(version,"^22\."), "22.23.4", true(), "EOL line")
| stats dc(host) as hosts values(host) as sample by version fixed


// node crash canary: more than a baseline of crashes per host per hour
FROM logs-*
| WHERE @timestamp > NOW() - 30 days
| WHERE (process.name == "node" OR process.executable LIKE "*/node" OR message LIKE "*node[*")
    AND (message LIKE "*SIGSEGV*" OR message LIKE "*SIGABRT*" OR message LIKE "*SIGBUS*")
| STATS count = COUNT(*) BY t = BUCKET(@timestamp, 1 hour), host.name
| WHERE count > 3

// node spawning shells (exclude build/CI hosts where this is routine)

FROM logs-*
| WHERE @timestamp > NOW() - 30 days
| WHERE process.parent.name IN ("node", "nodejs")
    AND (process.name IN ("sh", "bash", "dash", "curl", "wget", "perl") OR process.name LIKE "python*")
| WHERE NOT CIDR_MATCH(host.ip, "<build/CI subnets>")
| STATS count = COUNT(*), cmds = VALUES(process.command_line) BY host.name, process.parent.name, process.name

// response-side exposure: servers fetching Brotli from new destinations

FROM logs-*
| WHERE @timestamp > NOW() - 7 days AND message LIKE "*content-encoding: br*"
| WHERE CIDR_MATCH(source.ip, "<server subnets>")
| STATS first_seen = MIN(@timestamp), count = COUNT(*) BY source.ip, destination.ip
| WHERE first_seen > DATE_TRUNC(1 day, NOW() - 7 days)

// version inventory (software-inventory or process events carrying a version string)

FROM logs-*
| WHERE @timestamp > NOW() - 7 days
| WHERE process.name IN ("node", "node.exe") AND package.version IS NOT NULL
| EVAL fixed = CASE(STARTS_WITH(package.version, "26."), "26.12.1", STARTS_WITH(package.version, "24."), "24.22.1", STARTS_WITH(package.version, "22."), "22.23.4", "EOL line")
| STATS hosts = COUNT_DISTINCT(host.name), sample = VALUES(host.name) BY package.version, fixed

Timeline

Sources

Node.js October 21 security releases (official) · Tenable CVE record · The Hacker News, Oct 22 · The Hacker News — Node2Shell, Oct 23 · SecurityOnline · Qualys — QID 389117, Oct 23

Public OSINT only — contains no internal data Marcus Liberto · source Lane and triggers per CIRT triage-flow draft Tracked story: Cutsign 10-22 → 10-23 (Node2Shell update)