← Back to Feed CACHED · 2026-10-23 02:39:49 · CACHE_KEY CVE-2026-105482
CVE-2026-105482 · CWE-626 · Disclosed 2026-10-21

Node.js dns.resolve hostname truncation via embedded NUL byte

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A bouncer who checks IDs but reads them with glasses that can't see past a smudge

CVE-2026-105482 is a NUL byte truncation flaw in Node.js's dns.resolve() family of functions (including dns.resolve4(), dns.reverse(), and their Promise variants). Because these functions pass JavaScript strings to the underlying c-ares C library — which treats \0 as a string terminator — an embedded NUL byte silently chops the hostname. Resolving example.com\0.trusted.corp actually queries only example.com. All three supported release lines are affected: 22.x (before 22.23.4), 24.x (before 24.22.1), and 26.x (before 26.12.1). The fix, committed by James M Snell as 6b78ea171d, replaces standard string validation with validateStringWithoutNullBytes, throwing ERR_INVALID_ARG_VALUE on any NUL-containing input. The vulnerability was reported by researcher hasanfleyah and patched on October 21, 2026.

Node.js labeled this HIGH, but published no CVSS score. That label is defensible for the specific subset of applications that implement DNS-based hostname allowlisting before making server-side requests — classic SSRF-defense-bypass territory. However, the HIGH label overstates the risk for the average Node.js deployment, which either hardcodes DNS targets or uses higher-level HTTP clients (like fetch() or axios) that don't route through raw dns.resolve() with attacker-controlled input. The NUL byte must survive through input parsing into a dns.resolve*() call, and the application must be performing hostname validation *before* resolution — a meaningful but minority pattern. For most Node.js workloads, this is a MEDIUM-severity issue with a narrow exploitation window.

"NUL byte in dns.resolve truncates hostnames, enabling SSRF protection bypass in apps that validate before resolving."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify a Node.js service accepting user-supplied URLs

The attacker finds a publicly accessible Node.js API endpoint that accepts URLs in a request body (JSON, form data, or query string). The endpoint performs a server-side operation such as URL unfurling, webhook delivery, link previewing, or proxy forwarding. This is common in webhook handlers, image fetchers, and integration platforms.
Conditions required:
  • Publicly reachable Node.js service accepting user-supplied URLs
  • Application processes the URL server-side (SSRF-adjacent pattern)
Where this breaks in practice:
  • Not all Node.js apps accept user-supplied URLs for server-side processing
  • Many apps use managed PaaS platforms that strip unusual bytes at the edge
Detection/coverage: Web Application Firewalls (WAFs) with NUL byte detection rules; application-layer logging of URL inputs
STEP 02

Inject NUL byte into hostname via JSON or URL encoding

The attacker crafts a request with an embedded NUL byte in the hostname: {"url": "https://evil.com\u0000.trusted.corp/callback"}. In JSON, \u0000 is a valid Unicode escape that produces a NUL byte in the JavaScript string. Alternatively, %00 in URL-encoded form data achieves the same. The NUL byte must survive through the application's input parsing layer into the dns.resolve*() call.
Conditions required:
  • Input channel supports NUL byte delivery (JSON \u0000, URL-encoded %00)
  • Application does not strip NUL bytes during input sanitization
Where this breaks in practice:
  • Many input validation libraries explicitly reject NUL bytes
  • HTTP/1.1 headers cannot carry NUL bytes — delivery must be via body or query params
  • Some URL parsers (WHATWG URL, new URL()) reject NUL bytes in hostnames before dns.resolve is reached
Detection/coverage: Input validation logging; WAF rules for %00 and \u0000 patterns; Nuclei template in Han5N/10-21-CVE-PoC repo
STEP 03

Application validates hostname — NUL byte passes the check

The application checks whether the full JavaScript string ends with .trusted.corp (or matches an allowlist pattern). Because JavaScript's String.prototype.endsWith() and regex operations process the entire string *including* the NUL byte, the validation sees evil.com\0.trusted.corp and concludes it ends with .trusted.corp. The check passes.
Conditions required:
  • Application performs hostname validation before DNS resolution
  • Validation logic operates on the full JS string (standard behavior)
Where this breaks in practice:
  • Applications using new URL() to parse the hostname may extract only the hostname portion, potentially catching malformed input
  • Some allowlist implementations check against resolved IPs rather than hostnames, making this bypass irrelevant
Detection/coverage: No scanner coverage for application-specific validation logic; manual code review required
STEP 04

dns.resolve() truncates at NUL, resolves wrong host

The validated string is passed to dns.resolve4() or similar. Node.js passes it to c-ares as a C string. C-ares stops reading at \0, so it resolves evil.com instead of evil.com\0.trusted.corp. The application then connects to the IP address returned for evil.com, believing it connected to a trusted host. In SSRF scenarios, the attacker can direct this to an internal IP (e.g., 169.254.169.254 cloud metadata) by controlling the DNS response for evil.com.
Conditions required:
  • Application uses dns.resolve* (not a higher-level client that handles DNS internally)
  • Attacker controls DNS for the truncated domain
Where this breaks in practice:
  • Many Node.js HTTP clients (fetch, axios, got) perform DNS resolution internally without exposing dns.resolve() to attacker input
  • Cloud providers increasingly block metadata endpoint access via IMDSv2 requiring tokens
  • Network-level SSRF protections (egress filtering) may block connections to internal IPs regardless of DNS bypass
Detection/coverage: Network monitoring for unexpected DNS queries; egress firewall alerts for internal IP connections from application tier; GreyNoise has no observed exploitation mass-scanning tags as of Oct 23, 2026
03 · Compensating Control

1
MEDIUM 5.8→IGNORE 0.0
SEVERITY REDUCED
Add NUL byte input validation before any dns.resolve* call — Insert if (hostname.includes('\0')) throw new Error('invalid hostname') at every code path where user input reaches dns.resolve*(). This is the exact mitigation the patch applies. Since the noisgate verdict is MEDIUM, there is no mitigation SLA — proceed directly to the 365-day remediation window via patching. However, this one-line check can be deployed immediately as defense-in-depth.
2
MEDIUM 5.8→IGNORE 0.0
SEVERITY REDUCED
Upgrade Node.js to patched versions (22.23.4 / 24.22.1 / 26.12.1) — The definitive fix. Under the noisgate remediation SLA for MEDIUM, deploy within 365 days. Given the low exploitation friction for vulnerable app patterns, prioritize apps that process user-supplied URLs. The patch rejects NUL bytes with ERR_INVALID_ARG_VALUE across all dns.resolve*, dns.reverse(), and Promise variants.
3
MEDIUM 5.8→LOW 2.5
SEVERITY REDUCED
Use WHATWG URL parser for hostname extraction before DNS operations — Parsing user-supplied URLs with new URL(input) and extracting .hostname strips or rejects NUL bytes in many cases, preventing them from reaching dns.resolve*(). This is a code-level architectural change that provides defense-in-depth independent of the Node.js version. Audit URL-processing code for direct string-to-dns.resolve patterns.
4
MEDIUM 5.8→LOW 2.0
SEVERITY REDUCED
Implement IP-based SSRF protection instead of hostname-based — Resolve the hostname first, then validate the *resolved IP address* against a blocklist (RFC 1918, link-local, metadata endpoints) before connecting. This architectural pattern is immune to hostname-level truncation attacks. Libraries like ssrf-req-filter or manual IP validation post-resolution eliminate this entire attack class.
5
MEDIUM 5.8→LOW 3.0
SEVERITY REDUCED
Enforce network-level egress filtering — Block outbound connections from application-tier hosts to internal networks, cloud metadata endpoints (169.254.169.254), and unauthorized external destinations at the firewall/security-group level. Even if SSRF bypass succeeds at the application layer, network controls prevent the connection from reaching sensitive targets.
What doesn't work
  • WAF NUL-byte rules alone — While WAFs can detect %00 in URLs, NUL bytes in JSON request bodies (\u0000) may pass through WAF inspection that only examines URL/header layers. WAFs reduce but do not eliminate the attack surface.
  • Upgrading c-ares independently — The vulnerability is in Node.js's string-to-C-string conversion boundary, not in c-ares itself. c-ares correctly handles C strings; the bug is that Node.js passed a string that should have been rejected. Upgrading c-ares alone does nothing.
  • Rate limiting or authentication — This is a logic bypass, not a brute-force or unauthorized-access issue. An authenticated user with legitimate access to a URL-processing endpoint can exploit this with a single request.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNot observed. Not on CISA KEV. No mass exploitation campaigns reported as of October 23, 2026. GreyNoise shows no scanning tags.
Proof of ConceptPublic PoC available. Han5N/10-21-CVE-PoC repo contains a runnable PoC in the CVE-2026-105482/poc/ directory, classified as *Category A: third-party publicly executable*. The PoC demonstrates dns.resolve4('example.com\u0000.trusted.corp') resolving example.com.
EPSS ScoreNot yet scored. FIRST EPSS API returns empty data — CVE was disclosed October 21, 2026 (2 days ago). Expect initial scoring within 1-2 weeks.
KEV StatusNot listed. No CISA Known Exploited Vulnerabilities entry.
CVSS Vector (noisgate estimate)CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:N → 5.8 MEDIUM. Network-accessible, high attack complexity (requires specific app architecture), no privileges, changed scope (SSRF impacts downstream resources), low C/I impact.
Affected VersionsNode.js 22.x (all versions before 22.23.4), 24.x (before 24.22.1), 26.x (before 26.12.1). All three currently supported release lines affected.
Fixed Versions22.23.4, 24.22.1, 26.12.1 — released October 21, 2026. Fix commit: 6b78ea171d by James M Snell. Distro backports expected within 1-2 weeks.
Scanning / ExposureNode.js has 350M+ monthly downloads in 2026, used by 98% of Fortune 500. However, the vulnerable code path (dns.resolve* with attacker-controlled hostnames) represents a narrow subset. No Shodan/Censys fingerprinting can identify vulnerable *application patterns* — this is a code-level issue, not an exposed-service issue.
Disclosure TimelineDisclosed and patched October 21, 2026 as part of the Node.js October 2026 security release (10 CVEs total). Coordinated release across all three supported lines.
Reporterhasanfleyah (independent researcher). Fix by James M Snell (jasnell), Node.js core collaborator.

Sources.

  1. Node.js October 2026 Security Releases (Official Advisory)
  2. Node.js Fix Commit 6b78ea171d — dns: validate resolve inputs for null bytes
  3. Lilting Technical Analysis — Node.js v24.22.1 HIGH CVEs
  4. TuxCare — Node.js October 2026 Security Fixes and Brotli RCE
  5. Han5N/10-21-CVE-PoC — Public PoC Repository
  6. The Hacker News — Node.js Security Vulnerabilities Coverage
  7. CyberSecurityNews — Node.js TLS, DNS, and Brotli Vulnerabilities
  8. CVE.org Record
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.8/10)

Why this verdict

  • Attack complexity is genuinely high. The attacker needs a Node.js application that (a) accepts user-supplied hostnames, (b) validates them with string operations before DNS resolution, and (c) passes them through dns.resolve*() rather than a higher-level HTTP client. Each condition narrows the vulnerable population significantly.
  • NUL byte delivery has real friction. While JSON \u0000 and URL-encoded %00 can deliver NUL bytes, many frameworks, URL parsers (WHATWG new URL()), and WAFs strip or reject them before they reach application code. HTTP headers cannot carry NUL bytes at all.
  • Impact is secondary, not primary. This CVE enables hostname validation bypass — it is an *SSRF enabler*, not SSRF itself. The actual blast radius depends entirely on what the application does after DNS resolution and whether downstream targets (internal services, cloud metadata) are reachable.
  • Role multiplier: In the *low-value role* (dev servers, static generators, CLI tools), this vulnerability is irrelevant — no attacker-controlled DNS input. In the *typical role* (API servers, web backends), only apps with URL-processing/proxy patterns are exposed. In the *high-value role* (webhook handlers, SSRF-protection middleware, API gateways), the chain succeeds and could enable SSRF → cloud credential theft → lateral movement. However, the high-value role represents a minority of the Node.js installed base (estimated <5% of Node.js deployments are custom SSRF-defense-implementing proxies), and the chain requires *additional* exploitation steps beyond this CVE (reachable internal targets, missing network-level egress controls, IMDSv1). This does not meet the ≥1% threshold for fleet-scale/domain-takeover outcome from this CVE alone.
  • No exploitation signal. No KEV listing, no observed in-the-wild exploitation, EPSS not yet scored. The public PoC demonstrates the truncation but not a weaponized SSRF chain.

Why not higher?

HIGH would be warranted if the NUL byte injection directly yielded SSRF without additional application-specific prerequisites, or if the vulnerability affected a component where attacker-controlled DNS input is *expected* (like a DNS server or proxy appliance). Here, the attacker must find a Node.js app that implements a specific hostname-validation-then-resolve pattern, survive NUL byte filtering, and have a useful target behind the SSRF. Each link in this chain has independent friction. The public PoC demonstrates truncation, not a full exploitation chain.

Why not lower?

LOW would undercount the real risk for the meaningful subset of Node.js applications that do perform DNS-based SSRF protection — webhook handlers, URL previewers, integration platforms. For those apps, this is a clean bypass of a security control with a trivial payload (\u0000). Node.js's massive installed base (98% of Fortune 500, 350M+ monthly downloads) means even a small percentage of vulnerable patterns translates to thousands of affected services. The public PoC lowers the attacker's research cost to zero.

06 · Verification

Crowdsourced verification payload.

Run this script on any host where Node.js is installed to check if the running version is patched against CVE-2026-105482. No special privileges required — it only checks the Node.js version number. Example: chmod +x check_cve_2026_105482.sh && ./check_cve_2026_105482.sh

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-105482 - Node.js dns.resolve NUL byte truncation check
# Checks whether the installed Node.js version is patched.
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

if ! command -v node &>/dev/null; then
  echo "UNKNOWN - Node.js is not installed on this host."
  exit 2
fi

NODE_VERSION=$(node -v 2>/dev/null | sed 's/^v//')
if [[ -z "$NODE_VERSION" ]]; then
  echo "UNKNOWN - Could not determine Node.js version."
  exit 2
fi

# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$NODE_VERSION"

# Patched versions:
#   22.x >= 22.23.4
#   24.x >= 24.22.1
#   26.x >= 26.12.1
# Odd majors (23, 25) are EOL and unpatched.

check_patched() {
  case "$MAJOR" in
    22)
      if [[ "$MINOR" -gt 23 ]] || { [[ "$MINOR" -eq 23 ]] && [[ "$PATCH" -ge 4 ]]; }; then
        return 0
      fi
      return 1
      ;;
    24)
      if [[ "$MINOR" -gt 22 ]] || { [[ "$MINOR" -eq 22 ]] && [[ "$PATCH" -ge 1 ]]; }; then
        return 0
      fi
      return 1
      ;;
    26)
      if [[ "$MINOR" -gt 12 ]] || { [[ "$MINOR" -eq 12 ]] && [[ "$PATCH" -ge 1 ]]; }; then
        return 0
      fi
      return 1
      ;;
    *)
      # Majors 23, 25 are EOL (no patch available)
      # Majors < 22 are EOL
      # Majors > 26 are future (presumably patched)
      if [[ "$MAJOR" -gt 26 ]]; then
        return 0
      fi
      echo "VULNERABLE - Node.js v${NODE_VERSION} is on an EOL release line with no patch available."
      exit 1
      ;;
  esac
}

if check_patched; then
  echo "PATCHED - Node.js v${NODE_VERSION} is not affected by CVE-2026-105482."
  exit 0
else
  echo "VULNERABLE - Node.js v${NODE_VERSION} is affected by CVE-2026-105482. Upgrade to 22.23.4 / 24.22.1 / 26.12.1 or later."
  exit 1
fi