
Pre-auth RCE nelle versioni 19.0.0, 19.1.0, 19.1.1 e 19.2.0 di React Server Components.
Questo repository contiene una riproduzione di Proof of Concept (PoC) per CVE-2025-55182, una vulnerabilità critica di sicurezza in React Server Components (RSC) che consente l'esecuzione di codice arbitrario non autenticata.
La vulnerabilità risiede nel modo in cui React Server Components deserializza le "Server Actions" dalle richieste dei client. In particolare, la funzione requireModule non convalidava che il nome dell'export richiesto fosse una proprietà diretta del modulo. Ciò consentiva agli aggressori di accedere alla proprietà constructor delle funzioni esportate, ottenendo un riferimento al costruttore globale Function, che può essere utilizzato per eseguire codice arbitrario.
Questo PoC utilizza un ambiente Node.js minimale per isolare la vulnerabilità nella libreria react-server-dom-webpack, garantendo che l'exploit dimostri il bug nella libreria stessa, NON una configurazione errata in un framework.
npmnpm install
[!NOTE] Il
package.jsonè bloccato alla versione vulnerabile19.0.0.
Questo script avvia un server HTTP semplice 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 malevolo al server.
# tty2
node exploit.js id
Dovresti vedere l'output del comando restituito nella risposta:
Output previsto:
Response: uid=0(root) gid=0(root) groups=0(root)
Perché la vulnerabilità si è verificata?
La funzione requireModule in ReactFlightDOMServerNode.js di fatto si fidava di qualsiasi name inviato dal client. Eseguiva moduleExports[metadata[NAME]] senza verificare se quella proprietà fosse effettivamente destinata ad essere esposta. Quindi se il client diceva "bro, voglio questa proprietà", il server rispondeva "certo! eccola, amico".
Perché permettere alle persone di accedere a qualsiasi proprietà è una cattiva idea?
Perché in pratica consente a chiunque di raggiungere la catena dei prototipi, persino il constructor, che è estremamente pericoloso. Se il modulo esporta una funzione (ad esempio module.exports = () => {}), allora il suo constructor è letteralmente il costruttore globale Function.
Perché ottenere il costruttore Function significa RCE?
Una volta che un aggressore ottiene il costruttore Function, può abusare della funzione "Bound Server Action". Associa una stringa contenente JavaScript malevolo ad esso (sostanzialmente trasformandola in new Function("codice malevolo")). E una volta eseguita, il server esegue qualsiasi codice inserito.
Perché React eseguirebbe effettivamente quella funzione malevola?
Perché le Server Actions possono essere attivate tramite ID. Se l'aggressore crea un payload con un Action ID che punta al riferimento module#constructor, React lo risolve come un'azione normale e lo esegue. Quell'"azione" è in realtà la loro funzione malevola.
Perché tutto questo non è stato convalidato?
Il sistema presumeva semplicemente che id e name dai metadati del Server Reference si riferissero sempre a export validi definiti dallo sviluppatore. Non c'era alcun controllo di sicurezza come hasOwnProperty per assicurarsi che la proprietà richiesta fosse effettivamente un export reale e non qualcosa ereditato dalla catena dei prototipi.
server.js invece di Next.js?Utilizzo un server.js semplice (e un helper webpack-runtime.js) per configurare manualmente il runtime di React Server Components. Questo ci permette di:
module.exports = fn). Un bundler reale potrebbe cambiare il modo in cui gli export vengono incapsulati, a seconda della sua configurazione.react-server-dom-webpack, non di Next.js.react-server-dom-webpack presuppone di essere eseguito all'interno di un bundle Webpack. Il nostro webpack-runtime.js fornisce le variabili globali che si aspetta (__webpack_require__, __webpack_chunk_load__).
Non si tratta di simulare la vulnerabilità, ma di fornire alla libreria il runtime minimo di cui ha bisogno per funzionare.C'è stata discussione su "Invalid PoCs" che funzionano solo se lo sviluppatore espone intenzionalmente cose pericolose 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 sospetto, nessun comando shell.
L'exploit esce completamente da quella funzione
La vulnerabilità permette all'aggressore 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, invece la deserializzazione insicura lo ha fatto per loro.
L'unico requisito reale è che il modulo esporti direttamente una funzione (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", che significa "dammi il costruttore".bound: punta al Chunk 2, che contiene gli argomenti.["console.log('nice try, diddy!')"]: la stringa di codice malevolo.Quando React deserializza:
user-profile-action..constructor => ottenendo il costruttore globale Function.new Function("console.log('nice try, diddy!')")E questa è la RCE!
Aggiorna immediatamente alle versioni corrette:
react-server-dom-webpack >= 19.0.1react-server-dom-parcel >= 19.0.1react-server-dom-turbopack >= 19.0.1La correzione introduce controlli hasOwnProperty per prevenire l'accesso alle proprietà ereditate e limita i caricamenti di file base64.
Se esegui questo PoC contro una versione corretta, il server si bloccherà o darà 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 di cui non possiedi o non hai il permesso esplicito di testare.
Rilasciato sotto DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE.