← Back to Feed CACHED · 2026-09-26 09:06:54 · CACHE_KEY CVE-2026-23869
CVE-2026-23869 · CWE-400 · Disclosed 2026-04-08

Denial of Service via Cyclic Deserialization in React Server Components Flight Protocol

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

Someone can spin your server's CPU in circles by mailing it a riddle that references itself

CVE-2026-23869 is a denial-of-service flaw in React Server Components' Flight protocol — the binary wire format React uses to stream server-rendered UI to the client. The vulnerable code lives in ReactFlightReplyServer.js, specifically in the createMap, createSet, and extractIterator functions, which fail to detect cyclic references during deserialization of incoming HTTP request payloads. An attacker sends a crafted POST to any Server Function endpoint (a.k.a. Server Action); the server enters a CPU-bound loop for roughly 60 seconds before throwing a catchable error. Affected packages are react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack in versions 19.0.0–19.0.4, 19.1.0–19.1.5, and 19.2.0–19.2.4. Downstream, Next.js 13.x through 16.x with App Router enabled inherits the bug. Fixed in React 19.0.5 / 19.1.6 / 19.2.5 and Next.js 15.5.15 / 16.2.3.

Meta scored this 7.5 HIGH, which is technically accurate per CVSS’s availability-only, unauthenticated-remote formula. In practice, the severity is softer than that label implies. The impact is availability-only — no code execution, no data leak, no lateral movement. Each malicious request burns one CPU core for ~60 seconds then *self-recovers* via a catchable exception; the process does not crash. Standard reverse-proxy rate limiting, WAF virtual patches (Fastly and Vercel both shipped them), and cloud auto-scaling blunt the attack significantly. There is no public PoC, no KEV listing, and EPSS sits at 1.55% — all pointing to low real-world exploitation pressure. For a fleet manager triaging against RCE and privilege-escalation vulns, this is a MEDIUM that belongs in the regular remediation cycle, not an emergency.

"Unauthenticated DoS via cyclic deserialization in React Server Components — real but recoverable"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify a Server Function endpoint

The attacker locates a Next.js App Router or custom RSC application exposing Server Functions (Server Actions). These endpoints accept POST requests and are discoverable through normal page interaction, JavaScript source inspection, or forced browsing to /_next/ routes. No authentication is required.
Conditions required:
  • Target runs React 19.x with Server Components enabled
  • Server Function endpoint is network-reachable
Where this breaks in practice:
  • Server Functions are only present in apps that opted into React 19 RSC — not all React apps use them
  • Apps behind CDN edge caching may not forward arbitrary POSTs to origin
Detection/coverage: Web application scanners (e.g., Nuclei templates) can fingerprint Next.js version headers (x-powered-by, x-nextjs-*). Shodan/FOFA dorks for Next.js are well-known.
STEP 02

Craft a cyclic deserialization payload

The attacker constructs an HTTP POST body containing a React Flight reply payload with cyclic object references (e.g., a Map whose values reference the Map itself). The payload format is specific to the React Flight wire protocol and requires understanding its binary encoding, but the logic is straightforward once the format is known.
Conditions required:
  • Knowledge of React Flight reply wire format
  • Ability to send arbitrary POST bodies
Where this breaks in practice:
  • No public PoC or exploit tool exists as of September 2026
  • The Flight wire format is not formally documented, requiring reverse-engineering from source
STEP 03

Send payload to trigger CPU exhaustion

The attacker POSTs the cyclic payload to the Server Function endpoint. The server’s createMap/createSet/extractIterator functions enter a CPU-bound loop attempting to resolve the cycle. A single request pins one CPU core at 100% for approximately 60 seconds before a catchable error terminates the operation. The process does not crash.
Conditions required:
  • Network access to the endpoint (typically internet-facing)
Where this breaks in practice:
  • Single request = single core for ~60s; sustained DoS requires repeated requests
  • Rate limiting at reverse proxy (nginx, Cloudflare, AWS ALB) throttles volume
  • Cloud auto-scaling can absorb moderate request bursts
Detection/coverage: CPU spike alerts, request-rate anomalies on RSC endpoints, WAF rules (Fastly/Vercel virtual patches already available).
STEP 04

Sustain denial of service

To maintain a meaningful outage, the attacker must continuously send crafted requests at a rate exceeding the target’s available CPU capacity. For a typical 4-core Node.js deployment, 4+ concurrent malicious requests would saturate all cores. The attacker needs to sustain this volume for the duration of the desired outage.
Conditions required:
  • Sufficient request volume to overcome rate limits and scaling
  • Sustained attacker bandwidth and infrastructure
Where this breaks in practice:
  • WAF virtual patches (Fastly, Vercel, Cloudflare) can block the specific payload pattern
  • IP-based rate limiting caps per-source volume
  • The error is catchable — application frameworks can implement retry/backoff logic that partially self-heals
  • Cloud auto-scaling adds capacity during CPU spikes
Detection/coverage: Standard DDoS monitoring, per-endpoint rate analytics, CPU utilization dashboards.
03 · Compensating Control

1
MEDIUM 5.5→LOW 3.5
SEVERITY REDUCED
Deploy rate limiting on Server Function endpoints — Configure your reverse proxy (nginx, Cloudflare, AWS ALB, Envoy) to cap POST request rates to RSC/Server Action endpoints (typically /_next/ paths or form-action URLs) at a reasonable threshold — e.g., 10–20 requests per second per source IP. This directly breaks step 4 of the attack path by preventing the sustained request volume needed for meaningful DoS. Since this is a MEDIUM verdict, there is no noisgate mitigation SLA — deploy as part of standard hardening within the 365-day remediation window.
2
MEDIUM 5.5→LOW 2.5
SEVERITY REDUCED
Enable WAF virtual patch — If you use Fastly Next-Gen WAF or Vercel’s platform WAF, enable the CVE-2026-23869 virtual patch rule. Both vendors shipped signature-based blocks within days of disclosure. This blocks the specific cyclic payload pattern at the edge before it reaches your application server, neutralizing the attack entirely for covered endpoints.
3
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Upgrade to patched React/Next.js versions — Update react-server-dom-webpack/parcel/turbopack to 19.0.5, 19.1.6, or 19.2.5. For Next.js, upgrade to 15.5.15 or 16.2.3. Run npm audit fix or npm update in your CI pipeline. This is the definitive fix. Per noisgate remediation SLA for MEDIUM, complete within 365 days, though earlier is better if your deployment pipeline supports it.
4
MEDIUM 5.5→LOW 4.0
SEVERITY REDUCED
Configure request timeouts on Node.js server — Set HTTP request processing timeouts (e.g., server.timeout = 30000 in Node.js, or equivalent in your framework config) to kill long-running request handlers before the full ~60s CPU burn completes. This reduces per-request impact from 60s to your timeout threshold, making sustained DoS harder to achieve.
What doesn't work
  • Network-level DDoS protection (e.g., AWS Shield, Cloudflare L3/L4) — This is an application-layer (L7) attack using valid HTTP POST requests, not a volumetric flood. L3/L4 DDoS mitigation will not detect or block it.
  • Authentication/authorization on Server Functions — While adding auth would prevent anonymous exploitation, React Server Components’ deserialization occurs *before* application-level auth middleware in most framework configurations. The CPU exhaustion triggers during payload parsing, not during business logic execution.
  • Node.js clustering/PM2 restart policies — The process does not crash, so restart-on-failure policies never trigger. The CPU simply stays pinned at 100% for ~60s then recovers normally.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo known exploitation. Not listed on CISA KEV. No campaigns or threat-actor usage reported as of September 2026.
Proof-of-ConceptNo public PoC available. No exploit code on GitHub, ExploitDB, or Nuclei templates. Exploitation requires crafting React Flight wire-format payloads, which raises the bar slightly.
EPSS Score0.01551 (1.55%) — low exploitation probability, well below the 6% threshold that typically signals active concern.
KEV StatusNot listed. No CISA Binding Operational Directive applies.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — unauthenticated remote with high availability impact but zero confidentiality/integrity impact. Classic DoS-only scoring.
Affected Versionsreact-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack: 19.0.0–19.0.4, 19.1.0–19.1.5, 19.2.0–19.2.4. Downstream: Next.js 13.x–16.x with App Router.
Fixed VersionsReact packages: 19.0.5, 19.1.6, 19.2.5. Next.js: 15.5.15, 16.2.3. npm audit fix or npm update pulls the fix.
Scanning / ExposureNext.js applications are widely discoverable via Shodan (http.component:next.js), FOFA, and Censys. However, only apps using Server Functions (Server Actions) are vulnerable — a subset of the Next.js install base.
Disclosure Date2026-04-08 (GitHub Security Advisory GHSA-479c-33wc-g2pg). Patches released same day.
ReporterNot publicly credited in the GHSA or React blog. CVE assigned by Meta as CNA.

Sources.

  1. GitHub Security Advisory GHSA-479c-33wc-g2pg
  2. MITRE CVE Record
  3. Vercel Changelog - Summary of CVE-2026-23869
  4. Fastly Blog - What is CVE-2026-23869
  5. CyCognito - React Server Components DoS via Cyclic Deserialization
  6. Red Hat Customer Portal - CVE-2026-23869
  7. CIRCL Vulnerability Lookup - GHSA-479c-33wc-g2pg
  8. OSV - GHSA-479c-33wc-g2pg
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.5/10)

Why this verdict

  • Availability-only impact caps severity. The CVSS vector explicitly shows C:N/I:N — no data breach, no code execution, no lateral movement. For a fleet manager, this means the blast radius is *service degradation* on a single application, not credential theft or domain compromise.
  • Self-recovering behavior. Each malicious request burns ~60 seconds of CPU then throws a *catchable* error. The Node.js process does not crash or restart. This is a transient resource exhaustion, not a persistent denial of service.
  • Standard compensating controls are highly effective. Reverse-proxy rate limiting, WAF virtual patches (Fastly and Vercel shipped them within days), and cloud auto-scaling all directly counter this attack. Most production Next.js deployments already have at least one of these in place.
  • No exploitation pressure. EPSS is 1.55%, no KEV listing, no public PoC, no observed campaigns. The attack requires knowledge of an undocumented wire format with no available tooling.
  • Role multiplier: React Server Components power web application tiers — customer-facing UIs, dashboards, e-commerce storefronts. These are *not* canonical high-value infrastructure roles (not IdP, DC, hypervisor, PAM, backup, CI/CD, or network edge). A DoS on a web app tier does not produce domain takeover, fleet compromise, or supply-chain pivot. The worst plausible high-value outcome is temporary revenue-impacting outage on a customer-facing app, which is a business-continuity concern but not a security-boundary breach. The floor rule does not elevate this beyond MEDIUM.
  • Wide deployment is a wash. Next.js is extremely popular, which increases the *population* of potentially affected apps but does not change the per-app impact ceiling. Many Next.js apps don’t use Server Functions at all, narrowing the truly vulnerable subset.

Why not higher?

Upgrading to HIGH would require either a path to code execution, data compromise, or a blast radius beyond the individual application — none of which exist here. The DoS is temporary, self-recovering, and trivially mitigated by rate limiting. No exploitation in the wild and no PoC further reduce urgency. A HIGH rating would misallocate emergency patching resources away from RCE and privilege-escalation vulns that genuinely threaten fleet integrity.

Why not lower?

Dropping to LOW would understate the risk for internet-facing production applications that lack rate limiting. An unauthenticated attacker *can* degrade service availability with minimal effort once the payload format is understood. The wide deployment of Next.js means a non-trivial number of applications are exposed, and the attack requires no credentials or user interaction. MEDIUM correctly signals this belongs in the regular remediation cycle rather than the backlog.

06 · Verification

Crowdsourced verification payload.

Run this script on any host where a Node.js application using React Server Components is deployed. Point it at the application root directory (where node_modules lives). Requires read access to the filesystem; no elevated privileges needed. Example: bash cve-2026-23869-check.sh /opt/myapp

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/bin/bash\n# CVE-2026-23869 Checker - React Server Components DoS\n# Checks installed versions of affected react-server-dom-* packages\n# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN\n\nAPP_DIR=\"${1:-.}\"\nVULN=0\nCHECKED=0\nPACKAGES=(\"react-server-dom-webpack\" \"react-server-dom-parcel\" \"react-server-dom-turbopack\")\n\nif [ ! -d \"$APP_DIR/node_modules\" ]; then\n  echo \"UNKNOWN - no node_modules found at $APP_DIR\"\n  exit 2\nfi\n\nfor PKG in \"${PACKAGES[@]}\"; do\n  PKG_JSON=\"$APP_DIR/node_modules/$PKG/package.json\"\n  if [ -f \"$PKG_JSON\" ]; then\n    VERSION=$(node -e \"console.log(require('$PKG_JSON').version)\" 2>/dev/null)\n    if [ -z \"$VERSION\" ]; then\n      continue\n    fi\n    CHECKED=1\n    MAJOR=$(echo \"$VERSION\" | cut -d. -f1)\n    MINOR=$(echo \"$VERSION\" | cut -d. -f2)\n    PATCH=$(echo \"$VERSION\" | cut -d. -f3)\n    if [ \"$MAJOR\" -eq 19 ]; then\n      if [ \"$MINOR\" -eq 0 ] && [ \"$PATCH\" -le 4 ]; then\n        echo \"VULNERABLE - $PKG@$VERSION (fix: 19.0.5)\"\n        VULN=1\n      elif [ \"$MINOR\" -eq 1 ] && [ \"$PATCH\" -le 5 ]; then\n        echo \"VULNERABLE - $PKG@$VERSION (fix: 19.1.6)\"\n        VULN=1\n      elif [ \"$MINOR\" -eq 2 ] && [ \"$PATCH\" -le 4 ]; then\n        echo \"VULNERABLE - $PKG@$VERSION (fix: 19.2.5)\"\n        VULN=1\n      else\n        echo \"PATCHED - $PKG@$VERSION\"\n      fi\n    else\n      echo \"PATCHED - $PKG@$VERSION (outside affected major)\"\n    fi\n  fi\ndone\n\nif [ \"$CHECKED\" -eq 0 ]; then\n  echo \"UNKNOWN - none of the affected packages found in $APP_DIR\"\n  exit 2\nfi\n\nif [ \"$VULN\" -eq 1 ]; then\n  exit 1\nelse\n  echo \"PATCHED - all detected packages at safe versions\"\n  exit 0\nfi