
Pre-auth RCE in React Server Components-Versionen 19.0.0, 19.1.0, 19.1.1 und 19.2.0.
Dieses Repository enthält eine PoC-Reproduktion von CVE-2025-55182, einer kritischen Sicherheitslücke in React Server Components (RSC), die eine nicht authentifizierte Ausführung beliebigen Codes ermöglicht.
Die Schwachstelle liegt in der Art, wie React Server Components „Server Actions“ aus Client-Anfragen deserialisieren. Konkret hat die Funktion requireModule nicht validiert, dass der angeforderte Exportname eine direkte Eigenschaft des Moduls war. Dadurch konnten Angreifer auf die constructor-Eigenschaft exportierter Funktionen zugreifen und eine Referenz auf den globalen Function-Konstruktor erhalten, der zur Ausführung beliebigen Codes genutzt werden kann.
Dieser PoC verwendet eine minimale Node.js-Umgebung, um die Schwachstelle in der Bibliothek react-server-dom-webpack zu isolieren – nur um sicherzustellen, dass der Exploit den Fehler in der Bibliothek selbst demonstriert, NICHT eine Fehlkonfiguration in einem Framework.
npmnpm install
[!NOTE] Die
package.jsonist auf die verwundbare Version19.0.0gepinnt.
Dieses Skript richtet einen rohen HTTP-Server ein, der die verwundbare React-Laufzeit zum Dekodieren von Anfragen verwendet.
# tty1
node --conditions react-server server.js
Führen Sie in einem separaten Terminal den Exploit aus. Dieser sendet eine bösartige Flight-Payload an den Server.
# tty2
node exploit.js id
Sie sollten die Befehlsausgabe in der Antwort sehen:
Erwartete Ausgabe:
Response: uid=0(root) gid=0(root) groups=0(root)
Warum ist die Schwachstelle aufgetreten?
Die Funktion requireModule in ReactFlightDOMServerNode.js hat im Grunde jedem name vertraut, den der Client gesendet hat. Sie führte moduleExports[metadata[NAME]] aus, ohne zu prüfen, ob diese Eigenschaft tatsächlich exponiert werden sollte. Wenn der Client also sagte: „Alter, ich will diese Eigenschaft“, antwortete der Server einfach: „Klar, kein Problem! Hier hast du sie, Kumpel.“
Warum ist es eine schlechte Idee, Menschen Zugriff auf beliebige Eigenschaften zu geben?
Weil dadurch jeder in die Prototypenkette eindringen kann, sogar auf den constructor, was extrem gefährlich ist. Wenn das Modul zufällig eine Funktion exportiert (wie module.exports = () => {}), dann ist dessen constructor buchstäblich der globale Function-Konstruktor.
Warum bedeutet der Zugriff auf den Function-Konstruktor RCE?
Sobald ein Angreifer den Function-Konstruktor in die Hände bekommt, kann er die Funktion „Bound Server Action“ missbrauchen. Er bindet eine Zeichenkette mit bösartigem JavaScript daran (was im Grunde zu new Function("evil code") wird). Und sobald das ausgeführt wird, führt der Server jeden Code aus, den sie hineingelegt haben.
Warum würde React diese bösartige Funktion tatsächlich ausführen?
Weil Server Actions per ID ausgelöst werden können. Wenn der Angreifer eine Payload mit einer Action-ID erstellt, die auf seine module#constructor-Referenz zeigt, löst React sie wie eine normale Aktion auf und führt sie aus. Diese „Aktion“ ist eigentlich seine bösartige Funktion.
Warum wurde das alles nicht validiert?
Das System ging einfach davon aus, dass sich id und name aus den Server-Referenz-Metadaten immer auf gültige, vom Entwickler definierte Exporte beziehen. Es gab keine Sicherheitsprüfung wie hasOwnProperty, um sicherzustellen, dass die angeforderte Eigenschaft ein echter Export ist und nicht etwas, das von der Prototypenkette geerbt wurde.
server.js statt Next.js?Ich verwende ein rohes server.js (und ein Hilfsskript webpack-runtime.js), um die React-Server-Components-Laufzeit manuell zu konfigurieren. Das erlaubt uns:
module.exports = fn)s. Ein echtes Bundler-Tool könnte je nach Konfiguration ändern, wie Exporte verpackt werden.react-server-dom-webpack liegt, nicht in Next.js.react-server-dom-webpack geht davon aus, dass es in einem Webpack-Bundle läuft. Unser webpack-runtime.js liefert die erwarteten Globals (__webpack_require__, __webpack_chunk_load__).
Das ist kein Mocking der Schwachstelle, sondern gibt der Bibliothek nur die minimale Laufzeit, die sie tatsächlich benötigt, um zu funktionieren.Es gab Diskussionen über „Invalid PoCs“, die nur funktionieren, wenn der Entwickler absichtlich gefährliche Dinge wie child_process.exec exponiert.
Dieser PoC gehört nicht dazu. Er funktioniert mit einem normalen, sicheren Setup.
Die exponierte Funktion ist harmlos
Die App exponiert eine einfache updateProfile-Funktion, die nur eine Zeichenkette zurückgibt und nichts Verdächtiges, keine Shell-Befehle.
Der Exploit entkommt dieser Funktion vollständig
Die Schwachstelle erlaubt es dem Angreifer, den sicheren Export zu ignorieren und direkt zu updateProfile.constructor zu springen, also dem globalen Function-Konstruktor.
Das Kernproblem ist der Eigenschaftszugriff
React hätte den Zugriff auf .constructor NICHT erlauben dürfen. Der Entwickler hatte nicht vor, den Function-Konstruktor zu exponieren – stattdessen hat die unsichere Deserialisierung das für ihn übernommen.
Die einzige wirkliche Voraussetzung ist, dass das Modul eine Funktion direkt exportiert (module.exports = fn), was in CommonJS und vielen Bundler-Setups sehr üblich ist.
Die Payload in exploit.js erstellt eine React-Flight-Nachricht mit drei Chunks:
id: "user-profile-action#constructor", also „gib mir den Konstruktor“.bound: verweist auf Chunk 2, der die Argumente enthält.["console.log('nice try, diddy!')"]: die bösartige Code-Zeichenkette.Wenn React dies deserialisiert:
user-profile-action auf..constructor-Eigenschaft => erhält den globalen Function-Konstruktor.new Function("console.log('nice try, diddy!')")Und das ist die RCE!
Aktualisieren Sie sofort auf die gepatchten Versionen:
react-server-dom-webpack >= 19.0.1react-server-dom-parcel >= 19.0.1react-server-dom-turbopack >= 19.0.1Der Patch fügt hasOwnProperty-Prüfungen hinzu, um den Zugriff auf geerbte Eigenschaften zu verhindern, und schränkt base64-Datei-Uploads ein.
Wenn Sie diesen PoC gegen eine gepatchte Version ausführen, stürzt der Server ab oder gibt einen Fehler aus:
$ 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
Dies bestätigt, dass der Exploit nicht auf die constructor-Eigenschaft zugreifen konnte (sie gab undefined statt Function zurück) und daher der anschließende .bind-Aufruf fehlschlug.
Dieser Code dient nur zu Bildungs- und Testzwecken. Verwenden Sie diesen Exploit nicht gegen Systeme, die Ihnen nicht gehören oder für die Sie keine ausdrückliche Erlaubnis zum Testen haben.
Veröffentlicht unter der DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE.