
Exploit proof-of-concept per CVE-2025-55182, che dimostra RCE non autenticata in React Server Components tramite deserializzazione non sicura delle Server Actions, con passaggi di riproduzione dettagliati e indicazioni di mitigazione.
Questo repository contiene una riproduzione PoC di CVE-2025-55182, una vulnerabilità di sicurezza critica in React Server Components (RSC) che consente l'esecuzione arbitraria di codice senza autenticazione.
La vulnerabilità risiede nel modo in cui React Server Components deserializza le "Server Actions" dalle richieste dei client. Nello specifico, la funzione requireModule non riusciva a validare che il nome dell'export richiesto fosse una prop diretta del modulo. Questo permetteva agli attaccanti di accedere alla prop constructor delle funzioni esportate, ottenendo un riferimento al costruttore globale Function, che può essere usato per eseguire codice arbitrario.
Questo PoC utilizza un ambiente Node.js minimale per isolare la vulnerabilità nella libreria react-server-dom-webpack, giusto per assicurarsi che l'exploit dimostri il bug nella libreria stessa, NON una errata configurazione in un framework.
npmnpm install
[!NOTE] Il
package.jsonè fissato alla versione vulnerabile19.0.0.
Questo script configura un server HTTP grezzo che utilizza il runtime React vulnerabile per decodificare le richieste.
# tty1
node --conditions react-server server.js
In un terminale separato, esegui l'exploit. Questo invia un payload Flight dannoso al server.
# tty2
node exploit.js id
Dovresti vedere l'output del comando restituito nella risposta:
Output atteso:
Response: uid=0(root) gid=0(root) groups=0(root)
Perché si è verificata la vulnerabilità?
La funzione requireModule in ReactFlightDOMServerNode.js praticamente si fidava di qualunque name inviato dal client. Eseguiva moduleExports[metadata[NAME]] senza verificare che quella proprietà fosse effettivamente destinata a essere esposta. Quindi, se il client diceva "fratello, voglio questa proprietà", il server rispondeva "certo! eccola qui, amico".
Perché consentire alle persone di accedere a qualsiasi proprietà è una cattiva idea?
Perché in pratica consente a chiunque di accedere alla catena dei prototipi, persino al constructor, il che è estremamente pericoloso. Se il modulo esporta una funzione (come module.exports = () => {}), allora il suo constructor è letteralmente il costruttore globale Function.
Perché ottenere il costruttore Function significa RCE?
Una volta che un attaccante ottiene il costruttore Function, può abusare della funzionalità "Bound Server Action". Vi associa una stringa contenente JavaScript dannoso (trasformandola di fatto in new Function("evil code")). E una volta che viene eseguita, il server esegue qualunque codice vi sia inserito.
Perché React dovrebbe effettivamente eseguire quella funzione dannosa?
Perché le Server Actions possono essere attivate tramite ID. Se l'attaccante crea un payload con un Action ID che punta al riferimento module#constructor, React lo risolve come un'azione normale e lo esegue. Quella "action" è in realtà la loro funzione dannosa.
Perché nulla di tutto questo è stato validato?
Il sistema presumeva semplicemente che id e name dei metadati del Server Reference facessero sempre riferimento a export validi definiti dallo sviluppatore. Non c'era alcun controllo di sicurezza come hasOwnProperty per garantire che la prop richiesta fosse un export reale e non qualcosa ereditato dalla catena dei prototipi.
server.js invece di Next.js?Uso un server.js grezzo (e un helper webpack-runtime.js) per configurare manualmente il runtime di React Server Components. Questo ci consente di:
module.exports = fn)s. Un bundler reale potrebbe cambiare il modo in cui gli export vengono incapsulati, a seconda della sua config però.react-server-dom-webpack, non Next.js.react-server-dom-webpack presuppone di essere eseguito all'interno di un bundle Webpack. Il nostro webpack-runtime.js gli fornisce le variabili globali che si aspetta (__webpack_require__, __webpack_chunk_load__).
Questo non è simulare la vulnerabilità, è solo dare alla libreria il runtime minimo indispensabile per funzionare davvero.C'è stata una discussione su "PoC non validi" che funzionano solo se lo sviluppatore espone intenzionalmente roba pericolosa come child_process.exec.
Questo PoC non è uno di quelli. Funziona su una configurazione normale e sicura.
La funzione esposta è innocua
L'app espone una semplice funzione updateProfile che restituisce solo una stringa e niente di losco, nessun comando shell.
L'exploit sfugge completamente a quella funzione
La vulnerabilità consente all'attaccante di ignorare l'export sicuro e saltare direttamente a updateProfile.constructor, che è il costruttore globale Function.
Il problema principale è l'accesso alla proprietà
React NON avrebbe dovuto consentire l'accesso a .constructor. Lo sviluppatore non intendeva esporre il costruttore Function; è stata la deserializzazione insicura a farlo al posto suo.
L'unico requisito reale è che il modulo esporti una funzione direttamente (module.exports = fn), cosa molto comune in CommonJS e in molte configurazioni di bundler.
Il payload in exploit.js costruisce un messaggio React Flight con tre chunk:
id: "user-profile-action#constructor", ovvero "dammi il constructor".bound: punta al Chunk 2, che contiene gli argomenti.["console.log('nice try, diddy!')"]: la stringa di codice dannoso.Quando React deserializza questo:
user-profile-action..constructor => ottenendo il Function globale.new Function("console.log('nice try, diddy!')")E questo è l'RCE!
Effettua subito l'upgrade alle versioni patchate:
react-server-dom-webpack >= 19.0.1react-server-dom-parcel >= 19.0.1react-server-dom-turbopack >= 19.0.1La patch introduce controlli hasOwnProperty per impedire l'accesso alle proprietà ereditate e limita gli upload di file base64.
Se esegui questo PoC contro una versione patchata, il server andrà in crash o restituirà un errore con:
$ node --conditions react-server server.js
Listening on http://localhost:3000
/path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726
resolvedValue = resolvedValue.bind.apply(
^
TypeError: Cannot read properties of undefined (reading 'bind')
at /path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726:43
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.19.3
Questo conferma che l'exploit non è riuscito ad accedere alla proprietà constructor (ha restituito undefined invece di Function), e quindi la successiva chiamata .bind è fallita.
Questo codice è solo a scopo educativo e di test. Non utilizzare questo exploit contro sistemi che non possiedi o per cui non hai un'autorizzazione esplicita al test.
Rilasciato sotto DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE.