
Analisi tecnica dettagliata di CVE-2025-55182, una critica RCE non autenticata in Next.js + React 19.0.0. Documenta il percorso di ricerca, l'analisi della patch, il traversal dei prototipi e il sink di deserializzazione Blob che consente lo sfruttamento completo senza gadget specifici dell'applicazione.
Questo documento descrive il mio processo di ricerca personale su CVE-2025-55182, inclusi risultati confermati ed esperimenti che ho condotto. Rappresenta un resoconto onesto della mia indagine, incluse ipotesi iniziali errate e la svolta finale.
Dopo un'indagine approfondita su CVE-2025-55182 (CVSS 10.0), la mia conclusione iniziale era che l'RCE automatica non fosse stata dimostrata pubblicamente e che lo sfruttamento richiedesse gadget specifici dell'applicazione. Questa conclusione era errata.
Il 5 dicembre 2025, dopo aver ricevuto ulteriori approfondimenti da un altro ricercatore indipendente su X (@maple3142), ho riprodotto con successo RCE completa non autenticata su Next.js vanilla senza richiedere vulnerabilità di codice specifiche dell'applicazione.
Il mio focus iniziale era sul primo cambiamento nella patch di React 19.0.1:
Vulnerabile (19.0.0):
return fn.bind.apply(fn, [null].concat(_ref));
Corretto (19.0.1):
if (Array.isArray(promiseValue)) {
promiseValue = promiseValue.slice(0);
} else {
promiseValue = [];
}
Ho ipotizzato che il percorso dell'attacco fosse tramite fn.bind.apply() con oggetti malevoli invece di array. Sono riuscito a dimostrare l'iniezione di argomenti nelle Azioni del Server utilizzando $ACTION_REF_ con bound controllato dall'attaccante:
curl -X POST http://localhost:9000/ \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"<ACTION_ID>","bound":["; id #","/etc/passwd"]}'
Risultato: Gli argomenti sono stati iniettati con successo nell'Azione del Server. Tuttavia, questo porta a RCE solo se la funzione di destinazione utilizza tali argomenti in modo non sicuro.
Uno sguardo più approfondito alla patch ha rivelato un altro importante cambiamento in getOutlinedModel():
Vulnerabile:
for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
Corretto:
if (hasOwnProperty.call(value, name)) {
value = value[name];
}
Questo comportamento vulnerabile consentiva l'attraversamento della catena dei prototipi utilizzando riferimenti come:
$1:__proto__:constructor:constructor
Durante la ricerca, ho testato un oggetto thenable contenente una proprietà .then:
{"then": "$1:__proto__:constructor:constructor"}
Quando JavaScript elabora questo tramite await:
.then e tratta l'oggetto come una Promiseobj.then(resolve, reject)then risolve a Function.constructor, JavaScript tenta di eseguire Function(resolve, reject)Risultato osservato:
SyntaxError: Unexpected token 'function'
at Object.Function [as then] (<anonymous>)
Quando Function.constructor viene invocato come:
Function(resolve, reject)
// resolve.toString() = "function () { [native code] }"
// Function tenta di interpretare questo come codice → SyntaxError
Gli argomenti resolve e reject sono sempre le funzioni native di Promise. Function tenta di interpretare il primo argomento come codice sorgente, che è JavaScript non valido.
È qui che la mia ricerca si è fermata. Ho concluso che controllare gli argomenti di Function.constructor era impossibile senza un gadget specifico dell'applicazione.
Dopo aver pubblicato i miei risultati iniziali, un altro ricercatore indipendente mi ha indicato un pezzo critico che mi era sfuggito: il sink di deserializzazione $B (Blob).
Nel codice compilato del server React Flight (non visibile nelle fonti TypeScript), esiste:
case "B":
return response._formData.get(response._prefix + id);
Posizione:
[email protected]cjs/react-server-dom-webpack-server.node.unbundled.development.js[email protected]/dist/compiled/react-server-dom-webpack/Questo codice permette a React di chiamare response._formData.get() con valori derivati da input controllati dall'attaccante, senza validazione.
| Approccio | Argomenti per Function.constructor | Risultato |
|---|---|---|
| Thenable (Fase 3) | resolve, reject (funzioni native) | ❌ SyntaxError |
| Blob + Risposta Avvelenata | _prefix (stringa controllata dall'attaccante) |
Combinando:
$1:__proto__:then → Chunk.prototype.then)_response avvelenato con:
_formData.get impostato su Function.constructor_prefix impostato su codice JavaScript arbitrario$BIl gestore case "B": esegue:
Function.constructor("<codice attaccante>" + id)
Questo bypassa completamente la limitazione del binding degli argomenti.
Le PoC GitHub popolari che rivendicano RCE utilizzano ID di azione come:
"child_process#execSync""vm#runInThisContext"Queste sono false. Next.js accetta solo ID di azione definiti dall'applicazione. ID non validi producono:
TypeError: Cannot read properties of undefined (reading 'workers')
Tuttavia, il vero exploit non richiede ID di azione falsi. Qualsiasi ID di Azione del Server valido funziona.
Ho testato questa catena di sfruttamento su un'applicazione minima Next.js 15.0.3 + React 19.0.0 con solo:
async function myAction(data) {
"use server";
console.log("Server Action called with:", data);
return { success: true, received: data };
}
Risultato: RCE completa confermata. L'applicazione non conteneva codice non sicuro, nessun eval, nessun execSync, nessun gadget.
Al 5 dicembre 2025, il sink $B esiste ancora in Next.js 15.0.5 (la versione presumibilmente corretta).
Verifica:
$ npm pack [email protected]
$ tar -xzf next-15.0.5.tgz
$ grep -A5 'case "B":' package/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js
Risultato:
case "B":
return response._formData.get(response._prefix + obj);
Il codice è identico alla versione vulnerabile.
A causa della scoperta che la vulnerabilità potrebbe non essere completamente corretta nelle versioni dichiarate "fisse", sto trattenendo il payload proof-of-concept completo in attesa di verifica con i team di sicurezza di Vercel e Meta.
I dettagli tecnici forniti in questo documento sono sufficienti per comprendere il meccanismo della vulnerabilità ma intenzionalmente incompleti per prevenire uno sfruttamento immediato.
| Tecnica | Stato |
|---|---|
Iniezione di argomenti tramite bound | ✅ Confermato (impatto limitato) |
| Problema | Impatto |
|---|---|
Il sink di deserializzazione $B (Blob) | ❌ Critico - permette il controllo degli argomenti |
| Esaminare codice compilato vs. sorgente | ❌ Il sink esiste solo nell'output compilato |
| Meccanismo di avvelenamento dell'oggetto risposta | ❌ Permette di bypassare tutte le protezioni |
CVE-2025-55182 è sfruttabile per RCE completa non autenticata su applicazioni Next.js vanilla.
$B da un ricercatore indipendente$B accetta ancora oggetti _response arbitrariNext-Action insolite$@, __proto__, $B$B è stata fornita da un ricercatore su X (@maple3142)Ultimo aggiornamento: 5 dicembre 2025
| ✅ RCE |
| Aspetto | Risultato |
|---|
| Autenticazione richiesta? | ❌ No |
| Gadget applicativo richiesto? | ❌ No |
| Funziona su Next.js vanilla? | ✅ Sì |
| Numero di richieste necessarie | 1 POST |
| Versioni affette | Next.js ≤15.0.4 + React 19.0.0 |
| Punteggio CVSS | 10.0 (giustificato) |
| Attraversamento del prototipo |
| ✅ Confermato |
Accesso a Function.constructor tramite thenable | ✅ Confermato (ma non sfruttabile da solo) |
| Rilevamento di versioni vulnerabili | ✅ Confermato |