Prototype-pollution → Remote Code Execution via React Server Actions
Async, version-first, output-based canary detection - no blind guessing
Warning
For authorised security research and penetration testing only. Scanning systems without explicit written permission is illegal. The author assumes no responsibility for misuse of this tool.
- Vulnerability Overview
- Affected Versions
- How Detection Works
- Installation
- Usage
- Options Reference
- Output & Verdicts
- Browser Mode
- Version Detection Pipeline
React Server Actions pass action data as multipart form data directly into a server-side execution context. Due to insufficient prototype-pollution sanitisation in affected versions of react-server-dom-webpack, an attacker can:
- Inject a crafted multipart payload that pollutes
Object.prototype - Override
_response._prefixon the internal response object - Force the server to execute arbitrary shell commands via:
process.mainModule.require('child_process').execSync(cmd)
This gives unauthenticated Remote Code Execution on any publicly accessible Next.js application using React Server Actions with an affected version of React.
CVE-2025-55182 - Broad: affects React 19 packages (react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack)
CVE-2025-66478 - Strict: Next.js-specific exploitation path via the / Server Action endpoint
| Component | Vulnerable | Patched |
|---|---|---|
React react-server-dom-* |
19.0.0, 19.1.0, 19.1.1, 19.2.0 |
19.0.1, 19.1.2, 19.2.1+ |
| Next.js 16.x | 16.0.0 – 16.0.6 |
16.0.7+ |
| Next.js 15.x | 15.0.0-15.0.4, 15.1.0-15.1.8, 15.2.0-15.2.5, 15.3.0-15.3.5, 15.4.0-15.4.7, 15.5.0-15.5.6 |
15.0.5+, 15.1.9+, 15.2.6+, 15.3.6+, 15.4.8+, 15.5.7+ |
| Next.js 14.x | 14.3.0-canary.77+ canary builds only |
All stable 14.x - NOT affected |
| Next.js 13.x | - | NOT affected |
This scanner uses output-based canary verification - the most reliable method for confirming real code execution with zero false positives:
1. POST / with Next-Action header + multipart prototype-pollution payload
2. Payload executes: echo <random_canary>
3. Vulnerable server throws NEXT_REDIRECT with base64(canary) in the URL
4. Response header: X-Action-Redirect: /login?a=<base64(canary)>
5. Scanner decodes base64 → matches canary → RCE confirmed ✓
allow_redirects=False is critical - the confirmation lives in the header, not the redirect destination.
git clone https://github.com/InferiorAK/React2Shell
cd React2Shell
pip install -r requirements.txt
# Required only if using --browser mode for improved version detection:
playwright install chromium
playwright install-deps chromiumRequirements: Python 3.10+
# Single target
python3 CVE-2025-55182_scanner.py -u https://target.com
# Scan a list of URLs
python3 CVE-2025-55182_scanner.py -f urls.txt
# Quiet mode - only print RCE / Vulnerable results
python3 CVE-2025-55182_scanner.py -f urls.txt -q
# Browser mode - headless Chromium for accurate version detection
python3 CVE-2025-55182_scanner.py -f urls.txt -b
# Full options - recommended for large-scale scanning
python3 CVE-2025-55182_scanner.py -f urls.txt -b -q -c 3 -t 20| Flag | Long form | Default | Description |
|---|---|---|---|
-u |
--url |
- | Single target URL |
-f |
--file |
- | File containing one URL per line |
-o |
--output |
outputs/vuln.txt |
Output file for vulnerable targets |
-U |
--unknown-output |
outputs/unknown.txt |
Output file for unknown-version targets |
-c |
--concurrency |
5 |
Concurrent HTTP connections |
-t |
--timeout |
15 |
Per-request timeout (seconds) |
-q |
--quiet |
off | Only print RCE / VULNERABLE rows |
-b |
--browser |
off | Playwright headless Chromium version-detection fallback |
Each scanned target is assigned one of the following verdicts:
| Verdict | Color | Meaning |
|---|---|---|
RCE |
🔴 Red | Canary confirmed in X-Action-Redirect - proven remote code execution |
VULNERABLE |
🟠 Orange | Version is in vulnerable range; RCE payload did not echo back (WAF / patched config / wrong endpoint) |
Unknown |
🟡 Yellow | Next.js confirmed but version could not be detected - RCE attempted anyway; saved to outputs/unknown.txt for browser-mode retry |
Not Vulnerable |
🟢 Green | Version confirmed as patched, or RCE explicitly returned safe |
Skipped |
⚪ White | Confirmed-patched version or non-Next.js site - no PoC attempted |
Error |
⚪ White | Host unreachable or connection failed |
Output files (saved to outputs/ folder, created automatically):
outputs/vuln.txt—RCEandVULNERABLEresults (appended on each run)outputs/unknown.txt—Unknown-version targets; retry these with-bfor accurate version detection
Why does version show as
unknownfor 90%+ of sites?
Modern Next.js 13+ using the App Router architecture never embeds the framework version in static HTML. The version only exists in window.next.version after JavaScript executes in a real browser context.
Without --browser:
- Version detection relies on HTTP headers,
__NEXT_DATA__, inline scripts, and hashed chunk files - App Router sites almost always return
unknown- version-based filtering is bypassed
With --browser (-b):
- Playwright launches headless Chromium
- Page JavaScript fully executes
- Scanner reads
window.next.versiondirectly from the live DOM - Version accuracy goes from ~10% → near 100% on modern Next.js sites
# Recommended with browser mode - reduce concurrency (browser pages are resource-heavy)
python3 CVE-2025-55182_scanner.py -f urls.txt -b -c 3The scanner runs up to 7 stages, stopping as soon as a version is found:
Stage 1 │ HTTP Response Headers
│ X-Powered-By: Next.js X.Y.Z
│ x-nextjs-*, x-next-version
Stage 2 │ Inline <script> Blocks (per-block scan)
│ window.next = { version: "X.Y.Z", ... }
│ version:"X.Y.Z", router: ... ← minified bundle adjacent-key pattern
│ "nextVersion":"X.Y.Z"
│ Next.js vX.Y.Z ← plain text in meta / HTML comments
│ __NEXT_VERSION__ = "X.Y.Z"
Stage 3 │ __NEXT_DATA__ JSON Blob
│ Pages Router only - "version":"X.Y.Z" safe in this context
Stage 4 │ Hashed Chunk URLs (extracted from <script src>)
│ /_next/static/chunks/*, /_next/static/app/*, /_next/static/runtime/*
│ Fetches up to 12 real bundle files, scans for version strings
Stage 5 │ Fixed-name Fallback Chunks
│ main.js, framework.js, webpack.js, _app.js, pages/_app.js
Stage 6 │ /package.json endpoint
│ Dev mode / misconfigured servers expose this directly
Stage 7 │ Playwright Headless Browser [--browser / -b only]
│ Evaluates window.next.version after full JS execution
│ Only triggered when stages 1–6 return "unknown"
This tool is published for educational and authorised security research purposes only.
The author, InferiorAK, is not responsible for any damage caused by misuse.
Always obtain written permission before testing any system you do not own.
Made by InferiorAK
My Links

