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.
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.
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.
fetch()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.
| Line | Upgrade to | Note |
|---|---|---|
| 26.x (current) | 26.12.1 | all ten fixes |
| 24.x (LTS) | 24.22.1 | rejects the Node2Shell PoC input |
| 22.x (LTS) | 22.23.4 | oldest fixed line |
| 20.x and earlier | no fix | EOL April 30, 2026 — migrate |
| CVE | Sev | What |
|---|---|---|
| CVE-2026-111497 | High | Brotli 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-105483 | High | tls.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-106069 | High | Leading-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-105482 | High | dns.resolve*/reverse truncate at a NUL — attacker.example\0.trusted.example resolves as the attacker's name |
| CVE-2026-106072 | Med | Non-canonical IPv4 spellings (0x7f.0.0.1, 127.1, decimal int) let a DNS-only SAN authenticate an IP endpoint |
| CVE-2026-106074 | Med | Entropy cache saved into --build-snapshot snapshots / SEA binaries — randomUUID/randomInt repeat values across every process; rebuild those artifacts on a patched runtime |
| CVE-2026-105481 | Med | Keep-alive sockets accept responses written before the request is sent |
| CVE-2026-106070 | Low | zlib.ZipFile returns attacker-controlled bytes after header validation fails (26.x only) |
| CVE-2026-106071 | Low | Permission Model wildcard over-grant: --allow-fs-read=/etc/passwd.* also grants /etc/passwd (26.x only) |
| CVE-2026-106073 | Low | fs.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). | ||
| Category | Vendor | Status |
|---|---|---|
| Vendor | Node.js project | Fixed 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 scanners | Qualys | QID 389117 — covers CVE-2026-105481 / -105482 / -106069 / -106072 / -106073 / -106074 / -111497; flags builds below 22.23.4 / 24.22.1 / 26.12.1 |
| Tenable | No Nessus plugin yet for the October release — newest Node.js plugin still covers July 2026. CVE page is live (9.8 CVSS v3.1) | |
| Rapid7 | Nothing published — check InsightVM content release notes | |
| EDR / endpoint VM | CrowdStrike | Nothing published on Falcon/Spotlight coverage — verify in the console or with support |
| Microsoft Defender VM | Nothing published | |
| SentinelOne | Nothing published | |
| WAF / edge | Akamai | No 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 WAFs | Inbound request bodies only — never the outbound fetch() path; a rule is a stopgap, not a patch | |
| Cloud / container | Wiz / Orca / Trivy / Grype | Nothing 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 distros | Errata 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 | |
| Community | Nuclei / OSS | No 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.
npx is-my-node-vulnerable (official Node.js project tool)Accept-Encoding away from br and audit routes accepting Brotli bodiesNo 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
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