
Proof-of-Concept-Exploit für CVE-2025-55182, der eine nicht authentifizierte RCE in React Server Components durch unsichere Deserialisierung von Server Actions demonstriert, mit detaillierten Reproduktionsschritten und Eindämmungsanleitungen.
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 von beliebigem Code ermöglicht.
Die Schwachstelle besteht in der Art und Weise, wie React Server Components „Server Actions“ aus Client-Anfragen deserialisieren. Insbesondere validierte die Funktion requireModule nicht, ob der angeforderte Exportname eine direkte Eigenschaft des Moduls war. Dies erlaubte Angreifern, auf die constructor-Eigenschaft exportierter Funktionen zuzugreifen und so eine Referenz auf den globalen Function-Konstruktor zu erhalten, der zur Ausführung beliebigen Codes verwendet 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 und nicht eine Fehlkonfiguration in einem Framework.
npmnpm install
[!NOTE] Die
package.jsonist auf die verwundbare Version19.0.0festgelegt.
Dieses Skript richtet einen einfachen HTTP-Server ein, der die verwundbare React-Laufzeitumgebung 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 schädliche Flight-Nutzlast 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 vertraute im Grunde einfach jedem name, den der Client gesendet hat. Sie führte moduleExports[metadata[NAME]] aus, ohne zu prüfen, ob diese Eigenschaft tatsächlich offengelegt werden sollte. Wenn der Client also sagte „Bruder, ich will diese Eigenschaft“, sagte der Server einfach „Klar, hier hast du sie, Digga“.
Warum ist es eine schlechte Idee, den Zugriff auf beliebige Eigenschaften zu erlauben?
Weil es im Grunde jedem erlaubt, in die Prototypenkette zu gelangen, 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 erbeutet, kann er die Funktion „Bound Server Action“ missbrauchen. Er bindet einen String mit bösartigem JavaScript daran (macht daraus im Grunde new Function("böser Code")). Und sobald das ausgeführt wird, führt der Server den darin enthaltenen Code aus.
Warum würde React diese bösartige Funktion tatsächlich ausführen?
Weil Server Actions über eine ID ausgelöst werden können. Wenn der Angreifer eine Nutzlast mit einer Action-ID erstellt, die auf seine module#constructor-Referenz verweist, löst React diese wie eine normale Aktion auf und führt sie aus. Diese „Aktion“ ist dann eigentlich seine bösartige Funktion.
Warum wurde das alles nicht validiert?
Das System ging einfach davon aus, dass id und name aus den Metadaten der Server-Referenz immer auf gültige Exporte verweisen, die vom Entwickler definiert wurden. Es gab keine Sicherheitsprüfung wie hasOwnProperty, um sicherzustellen, dass die angeforderte Eigenschaft tatsächlich ein echter Export und nicht etwas aus der Prototypenkette ist.
server.js statt Next.js?Ich verwende ein einfaches server.js (und ein Hilfsskript webpack-runtime.js), um die React Server Components-Laufzeitumgebung manuell zu konfigurieren. Das ermöglicht uns:
module.exports = fn). Ein echter Bundler könnte die Art und Weise ändern, wie Exporte verpackt werden, abhängig von seiner Konfiguration.react-server-dom-webpack liegt, nicht in Next.js.react-server-dom-webpack geht davon aus, dass es in einem Webpack-Bundle läuft. Unsere webpack-runtime.js liefert die globalen Variablen, die es erwartet (__webpack_require__, __webpack_chunk_load__).
Dies ist kein Mocken der Schwachstelle, sondern gibt der Bibliothek nur die minimale Laufzeitumgebung, die sie tatsächlich zum Funktionieren braucht.Es gab Diskussionen über „Ungültige PoCs“, die nur funktionieren, wenn der Entwickler absichtlich gefährliche Dinge wie child_process.exec offenlegt.
Dieser PoC ist keiner von diesen. Er funktioniert in einem normalen, sicheren Setup.
Die exponierte Funktion ist harmlos
Die App exponiert eine einfache updateProfile-Funktion, die nur einen String zurückgibt und nichts Verdächtiges, keine Shell-Befehle.
Der Exploit verlässt diese Funktion vollständig
Die Schwachstelle erlaubt es dem Angreifer, den sicheren Export zu ignorieren und direkt zu updateProfile.constructor zu springen, 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 offenzulegen – die unsichere Deserialisierung hat das für ihn getan.
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 Nutzlast in exploit.js erstellt eine React-Flight-Nachricht mit drei Blöcken:
id: "user-profile-action#constructor", bedeutet „gib mir den Konstruktor“.bound: verweist auf Block 2, der die Argumente enthält.["console.log('nice try, diddy!')"]: der bösartige Code-String.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ührt hasOwnProperty-Prüfungen ein, um den Zugriff auf vererbte Eigenschaften zu verhindern, und schränkt base64-Datei-Uploads ein.
Wenn Sie diesen PoC gegen eine gepatchte Version ausführen, wird der Server mit einem Fehler abstürzen:
$ 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 ausschließlich zu Bildungs- und Testzwecken. Verwenden Sie diesen Exploit nicht gegen Systeme, die Sie nicht besitzen 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.