Kritische RCE-Schwachstelle CVE-2025-11953 gefährdet React-Native-Entwickler

Update vom 9. Februar 2026 – Unterschied bei den Auswirkungen vor Version 17.0.0 und ab Version 17.0.0 wurde klargestellt

Das JFrog Security Research Team hat vor Kurzem CVE-2025-11953 entdeckt und offengelegt – eine kritische (CVSS 9.8) Sicherheitslücke, die das äußerst beliebte NPM-Paket @react-native-community/cli betrifft, das ungefähr 2 Mio. wöchentliche Downloads hat.

Die Schwachstelle ermöglicht es nicht authentifizierten Angreifern aus der Ferne, die Ausführung beliebiger Betriebssystembefehle auf dem Rechner einfach auszulösen, auf dem der Entwicklungsserver von react-native-community/cli ausgeführt wird, was für Entwickler ein erhebliches Risiko darstellt.

React Native ist ein beliebtes Framework für die Entwicklung plattformübergreifender mobiler Apps mit JavaScript. Die Schwachstelle befindet sich in einem Paket, das Teil des umfassenderen Projekts React Native Community CLI ist, das vor einigen Jahren aus der zentralen React-Native-Codebasis ausgegliedert wurde, um die Wartbarkeit zu verbessern. Die CLI ist eine Sammlung von Befehlszeilentools, die Entwickler bei der Erstellung von React-Native-Mobilanwendungen unterstützen. Sie wird offiziell zum Erstellen von React-Native-Mobil-Apps ohne Verwendung eines Frameworks sowie von React Native für Windows, React Native für macOS und mehr verwendet.

Im Gegensatz zu typischen Schwachstellen in Entwicklungsservern, die nur vom lokalen Rechner eines Entwicklers aus ausnutzbar sind, setzt eine zweite Sicherheitslücke, die das Team in der Kerncodebasis von React Native entdeckt hat, den Entwicklungsserver externen Netzwerkangriffen aus – wodurch die erstgenannte Schwachstelle zu einem äußerst kritischen Problem wird.

Wir möchten dem Sicherheitsteam und den Ingenieuren von Meta für die umgehende Bearbeitung der Schwachstelle CVE-2025-11953 danken.

Wer ist von CVE-2025-11953 betroffen?

Entwickler, die ihr React Native-Projekt mit einer anfälligen Version von @react-native-community/cli initiiert haben und den Metro-Entwicklungsserver ausführen, indem sie einen der folgenden oder ähnliche Befehle verwenden, sind für CVE-2025-11953 anfällig:

 npm start
 npm run [start|android|ios|windows|macos]
 npx react-native [start|run-android|run-ios|run-windows|run-macos]
 npx @react-native-community/cli [start|run-android|run-ios|run-windows|run-macos]

 der Entwicklungsserver (Metro) von react-native läuft

Während die Schwachstelle standardmäßig angreifbar ist, wenn ein React-Native-Projekt mit @react-native-community/cli initiiert wird, ist es wichtig zu verstehen, dass nicht jeder Entwickler, der diese Bibliothek als Abhängigkeit installiert hat, zwangsläufig angreifbar ist.

Insbesondere sind Entwickler, die React Native mit einem Framework verwenden, das Metro nicht als Entwicklungsserver nutzt, in der Regel nicht gefährdet.

Die Schwachstelle betrifft direkt das Paket @react-native-community/cli-server-api in den Versionen 4.8.0 bis 20.0.0-alpha.2, und ist seit Version 20.0.0 behoben.

In den Versionen 4.8.0 bis 17.0.0 kann die Schwachstelle nicht authentifizierten Remote-Angreifern ermöglichen, ausführbare Dateien auszuführen, die sich bereits auf einem Remote-Rechner befinden (ohne dass Argumente angegeben werden müssen).Dies ist für Angreifer von Nutzen, die auf andere Weise manipulierte ausführbare Dateien auf den Remote-Rechner hochladen können.In den Versionen 17.0.0 bis 20.0.0-alpha.2 wird die Schwachstelle noch kritischer und kann zu einer vollständigen nicht authentifizierten Remote-Ausführung von Betriebssystembefehlen führen, deren Details wir hier vorstellen werden.

So prüfen Sie, ob das anfällige Paket in einem bestimmten NodeJS-Projekt vorhanden ist:

 cd 
 npm list @react-native-community/cli-server-api

Das Paket kann auch systemweit auf Ihrem System installiert sein. Dies können Sie überprüfen, indem Sie Folgendes ausführen:

 npm list -g @react-native-community/cli-server-api

Beachten Sie, dass das betroffene Paket üblicherweise zusammen mit @react-native-community/cli in passenden Versionen gebündelt wird. Daher sind Projekte, die @react-native-community/cli in den Versionen 4.8.0 bis 20.0.0‑alpha.2 verwenden, wahrscheinlich von anfälligen Versionen von @react-native-community/cli-server-api betroffen.

Unter Windows haben wir nachgewiesen, dass diese Schwachstelle zur Ausführung beliebiger Betriebssystembefehle (Shell-Befehle mit vollständiger Parameterkontrolle) führt. Unter macOS und Linux führt die Schwachstelle zur Ausführung beliebiger ausführbarer Dateien mit eingeschränkter Parameterkontrolle. Die Ausführung beliebiger Betriebssystembefehle auf diesen Plattformen könnte mit weiteren Untersuchungen möglich sein.

Wie kann CVE-2025-11953 behoben werden?

Durch die Durchführung der folgenden Schritte lässt sich die Sicherheitslücke CVE-2025-11953 beheben:

  • Aktualisieren Sie @react-native-community/cli-server-api in jedem Ihrer react-native-Projekte auf Version 20.0.0 oder höher, die eine Behebung dieser Sicherheitslücke enthält. Dies ist die empfohlene Lösung.
  • Zur Verbesserung der Sicherheit oder falls ein Upgrade nicht möglich ist, binden Sie den Entwicklungsserver explizit an die localhost-Schnittstelle, indem Sie das Flag „–host 127.0.0.1“ hinzufügen, wie in den folgenden Beispielen gezeigt:
 npx react-native start --host 127.0.0.1
 npx @react-native-community/cli start --host 127.0.0.1

Zusammenfassung von CVE-2025-11953

Der Metro-Entwicklungsserver, der von @react-native-community/cli gestartet wird, stellt standardmäßig eine Verbindung zu externen Schnittstellen her (was an sich schon ein kleines Sicherheitsrisiko darstellt).Der Endpunkt /open-url des Servers verarbeitet eine POST-Anfrage, die einen vom Benutzer eingegebenen Wert enthält, der an die unsichere Funktion open() übergeben wird, die vom Open-NPM-Paket bereitgestellt wird und zur Ausführung von Betriebssystembefehlen führt.

Dadurch kann ein nicht authentifizierter Netzwerkangreifer eine POST-Anfrage an den Server senden und beliebige Shell-Befehle mit vom Angreifer kontrollierten Parametern ausführen.

<code style=" width="400" height="364" />calc.exe wird als Ergebnis unseres Exploits ausgeführt

CVE-2025-11953: Technische Details

Bei der Erstellung eines React-Native-Projekts können Entwickler wählen, ob sie ein bestehendes Framework verwenden, wie etwa Expo, das die CLI des Frameworks verwendet und nicht anfällig ist, oder ihre App ohne Framework erstellen – wie wir es hier tun werden –, wenn sie lieber ihr eigenes Framework schreiben möchten oder bestimmte Einschränkungen haben, mit denen ein bestimmtes Framework kollidiert.

Der Entwicklungsprozess beginnt in der Regel mit der Verwendung der @react-native-community/cli, um mit dem Befehl npx @react-native-community/cli init MyApp ein neues Projekt zu initialisieren, wodurch die erforderliche Projektstruktur, Abhängigkeiten und Konfigurationsdateien eingerichtet werden. Während der aktiven Entwicklung verlassen sich Entwickler auf die CLI, um den Metro-Entwicklungsserver auszuführen (mit Befehlen wie npx react-native start), der das JavaScript-Bundle an die laufende App (auf einem Emulator oder Mobilgerät) bereitstellt und wesentliche Entwicklungsfunktionen wie Hot Reloading und Fast Refresh für schnelle Iterationen ermöglicht.

Beim Ausführen von npx react-native start leitet die cli.js von react-native den Befehl an das Paket @react-native-community/cli weiter (eine optionale Abhängigkeit von react-native), das seine Funktion build\index.js::setupAndRun() ausführt. Diese Funktion lädt die CLI-Befehle (d. h. start-Befehl), indem sie loadConfigAsync aufruft, wodurch während des Vorgangs die Datei node-modules/react-native/react-native.config.js gefunden und verwendet wird.

 const commands = [];
 const {
   bundleCommand,
   startCommand,
 } = require('@react-native/community-cli-plugin');
 commands.push(bundleCommand, startCommand);

node-modules/react-native/react-native.config.js – fügt startCommand zu einer exportierten Befehlsliste hinzu, die dann zur CLI-Befehlsliste hinzugefügt wird.

Die Datei react-native.config.js importiert startCommand aus @react-native/community-cli-plugin – diese Funktion ist für das Ausführen des Entwicklungsservers zuständig.

 import runServer from './runServer';
 /* ... */
 const startCommand: Command = {
   name: 'start',
   func: runServer,
   description: 'Start the React Native development server.',

Snippet aus der Datei index.js des Startbefehls

Wenn der start-Befehl ausgeführt wird, ruft er runServer.js auf:

 /* ... */

 import {createDevServerMiddleware} from './middleware'; /* *** 1 *** */

 /* ... */

 /* *** 2 *** */
 const { middleware: communityMiddleware } = createDevServerMiddleware({
     host: hostname,
     port,
     watchFolders,
   });

 /* ... */

   /* *** 3 *** */
   await Metro.runServer(metroConfig, {
     host: args.host,
     secure: args.https,
     secureCert: args.cert,
     secureKey: args.key,
     unstable_extraMiddleware: [communityMiddleware, middleware],
     websocketEndpoints: {
       ...communityWebsocketEndpoints,
       ...websocketEndpoints,
     },
   });

Vereinfachte Version von runServer.js

runServer.js führt Folgendes aus:

  1. Importiert die Funktion createDevServerMiddleware aus ./middleware, die sie aus der index.ts von @react-native-community/cli-server-api importiert.
  2. Führt createDevServerMiddleware aus, wodurch Middleware erstellt wird – eine Funktion, die einem HTTP-Server hinzugefügt wird, eingehende Anfragen abfängt, Funktionen hinzufügt und sie dann an den nächsten Handler weitergibt. In unserem Fall ist der hinzugefügte Handler, der uns interessiert, /open-url, der vom Handler openURLMiddleware verarbeitet wird. Die Middleware wird der Variablen communityMiddleware zugewiesen.
  3. Startet den Metro-Server mit communityMiddleware als Parameter. Dies ist der Entwicklungsserver der CLI.

Sehen wir uns nun den openURLMiddleware -Handler an, der im Rahmen von communityMiddleware zum Server hinzugefügt wurde:

 async function openURLMiddleware(req: IncomingMessage, ...) {
   if (req.method === 'POST') {

     /* ... */

     const {url} = req.body as {url: string};

     await open(url);

     /* ... */

   }
   next();
 }
 export default connect().use(json()).use(openURLMiddleware);

Vereinfachtes Snippet der openURLMiddleware-Funktion aus openURLMiddleware.ts

Diese Funktion wird mit einem HTTP-Anfrageparameter req aufgerufen, bei dem es sich um eine JSON-POST-Anfrage mit einem Zeichenfolgenfeld url handelt. Sie nimmt den Wert des Felds url, bei dem es sich um eine nicht bereinigte Zeichenfolge handelt, die direkt vom Benutzer stammt, und verwendet ihn als Parameter für den Aufruf open().

  1. Die Funktion open() wird aus dem npm-Paket ‚open‘ in Version 6.4.0 importiert.
  2. open(url) führt auf Windows-Rechnern Folgendes aus:
    1. Setzt den auszuführenden Befehl auf „cmd“
    2. Erstellt das Array cliArguments: ['/c', 'start', '""', '/b']
    3. Maskiert & mit ^ im Ziel-String, der die von uns angegebene URL ist.
    4. Fügt die Zeichenfolge target zu den cliArguments hinzu
    5. Führt den folgenden Befehl aus:
      childProcess.spawn(command, cliArguments, childProcessOptions);

Während der Befehl start aufgrund seines HTTP-URI-Schemas einen einzigen URL-Parameter akzeptieren und diesen im Standardbrowser öffnen kann, akzeptiert er auch das folgende Format zum Ausführen von Befehlen:
start “title” /b command optional_command_parameters

Im Fall von open(“calc.exe”) wird der folgende neue Prozess gestartet:

 cmd /c start “” /b calc.exe

Dies führt zur Ausführung von calc.exe.

Um einen beliebigen Befehl auszuführen, könnten wir den Parameter url von open auf einen cmd /c -Befehl setzen. Zum Beispiel mit dem folgenden Ergebnis:

 cmd /c start "" /b cmd /c echo abc > c:\temp\pwned.txt

React Native CVE – image5React Native CVE - image5
Eine neue Datei pwned.txt, die als Ergebnis unseres Befehls cmd.exe /c in C:\temp erstellt wurde – was beweist, dass die Ausführung eines beliebigen Codes erfolgreich war.

Angriffsflächen auf verschiedenen Betriebssystemen

Im Gegensatz zu Windows nutzt der Befehl open, wie oben gezeigt, unterschiedliche Codepfade auf macOS und Linux und führt dabei jeweils open und xdg-open aus. Bei unixähnlichen Betriebssystemen erwartet der erzeugte Prozess, dass jedes Argument eine separate Zeichenfolge in cliArguments ist, was sich von Windows unterscheidet, wo der erzeugte Prozess eine einzelne Argumentzeichenfolge lpCommandLine erwartet, die zusammengefügt wird, bevor der Aufruf von CreateProcess erfolgt.

Darüber hinaus werden beide Befehle ohne Shell ausgeführt. In Kombination mit der Tatsache, dass wir nur eine einzige Zeichenfolge (url) kontrollieren, lassen diese Faktoren es nicht ohne Weiteres zu, beliebige Befehle auf macOS und Linux auszuführen.

Sowohl xdg-open als auch open bestimmen, ob die Eingabezeichenfolge eine Datei oder ein URI ist, und handeln (verteilen) entsprechend.

Die Angriffsfläche zur Erzielung von Codeausführung in diesen Betriebssystemen kann von der Konfiguration des Systems abhängen und umfasst typischerweise:

  • Ermittlung eines URI-Schemas, dessen Handler dabei helfen kann, einen Schritt bei der Ausnutzung zu erreichen (entweder als Funktion oder durch Ausnutzung einer Schwachstelle im Handler selbst).
  • Ausführen einer lokalen Datei über ein reguläres oder ein file://-URI-Schema. (Könnte optional mit einer Datei-Dropper-Primitive kombiniert werden.)
  • Ausführung einer Remote-Datei auf dem Server des Angreifers mithilfe von smb://-, dav://-URI-Schemata oder ähnlichen. (Wird wahrscheinlich Sicherheitsdialoge auslösen, in denen der betroffene Benutzer um Bestätigung gebeten wird.)

Zusammenfassend lässt sich sagen, dass die Ausführung beliebiger Betriebssystembefehle auf diesen Plattformen mit weiteren Untersuchungen möglich sein könnte.

Sollte der Entwicklungsserver nicht nur an „localhost“ gebunden sein?

Wie wir bereits gezeigt haben, wird beim Ausführen des Metro-Entwicklungsservers ausdrücklich angegeben, dass er über die localhost-Schnittstelle gestartet wird:

React Native CVE – image4

In der Realität sehen wir jedoch, dass der Server auf allen Schnittstellen lauscht! (0.0.0.0 und IPv6 [::]):

React Native CVE - image2

Wie bereits erwähnt, liegt dies an einer weiteren Schwachstelle, die wir in @react-native/community-cli-plugin entdeckt und offengelegt haben und die als „Informative“ eingestuft wurde.

Durch Ausführen von npx react-native start gelangt der Code zur Funktion runServer.js::runServer :

 async function runServer(
   _argv: Array,
   cliConfig: Config,
   args: StartCommandArgs,
 ) {

   /* ... */

   const hostname = args.host?.length ? args.host : 'localhost'; // *** 1 ***

   /* ... */

   const protocol = args.https === true ? 'https' : 'http';
   const devServerUrl = url.format({protocol, hostname, port}); // *** 2 ***

   /* ... */

   console.info(`Starting dev server on ${devServerUrl}\n`); // *** 3 ***

   /* ... */

   await Metro.runServer(metroConfig, {
     host: args.host, // *** 4 ***

Relevante Teile der runServer -Funktion aus runServer.js

Wir gehen davon aus, dass der Startbefehl mit den Standardwerten und ohne optionale Parameter aufgerufen wird. Gehen wir die einzelnen Schritte im obigen Codebeispiel durch:

    1. hostname enthält den Parameter host, sofern angegeben, andernfalls "localhost".
    2. devServerUrl wird unter Verwendung des Hostnamens gebildet.
    3. Die irreführende Meldung “Starting dev server on http://localhost:8081” wird ausgegeben.
    4. Die Funktion Metro.runServer, die für das Ausführen des Metro-Entwicklungsservers zuständig ist, wird mit dem Host args.host aufgerufen, der undefined ist, anstatt die Variable hostname zu verwenden.

Später verwendet der Code von Metro für runServer diesen undefinierten Parameter beim Aufruf von httpServer.listen

Aus der Dokumentation von node zu server.listen von http geht hervor, dass sich diese Funktion wie server.listen von net.Server verhält, wo es heißt:

„Wenn host weggelassen wird, akzeptiert der Server Verbindungen auf der nicht angegebenen IPv6-Adresse (::), wenn IPv6 verfügbar ist, oder andernfalls auf der nicht angegebenen IPv4-Adresse (0.0.0.0). In den meisten Betriebssystemen kann das Lauschen auf der nicht angegebenen IPv6-Adresse (::) dazu führen, dass net.Server auch auf der nicht angegebenen IPv4-Adresse (0.0.0.0) lauscht.“

Die explizite Übergabe von undefined ist funktional gleichbedeutend mit dem Weglassen des Parameters. Daher lauscht der Server standardmäßig auf externe Verbindungen.

Infolgedessen sind die HTTP-Endpunkte, die nur für die Entwicklung bestimmt sind, für Netzwerkangreifer zugänglich.

Zusammenfassung

Diese Sicherheitslücke zeigt, dass selbst unkomplizierte Schwachstellen zur Remotecodeausführung, etwa die Übergabe von Benutzereingaben an die System-Shell, in realer Software weiterhin vorkommen, insbesondere wenn sich die gefährliche Sink-Funktion tatsächlich in Drittanbietercode befindet – in diesem Fall die importierte Funktion „open“. Sie erinnert daran, dass Verfahren für sicheres Coding und automatisiertes Sicherheitsscanning unerlässlich sind, um diese leicht ausnutzbaren Schwachstellen zu verhindern, bevor sie in die Produktionsumgebung gelangen.

Eine gute Möglichkeit, diese und ähnliche Sicherheitslücken im eigenen Code zu vermeiden, besteht im Einsatz des SAST-Scannings von JFrog Advanced Security, das Entwicklern ermöglicht, Sicherheitsprobleme früh im Entwicklungsprozess zu erkennen und zu beheben. Ohne erforderliche Konfiguration integriert sich JFrog SAST nahtlos über IDE-Plug-ins und CLI-Tools in bestehende Workflows und ermöglicht Entwicklern einfachen Zugriff auf umsetzbare Erkenntnisse, ohne ihren Arbeitsfluss zu unterbrechen.

So sehen wir beispielsweise im folgenden Screenshot, wie JFrog SAST die oben genannte Sicherheitslücke beim Scannen als Erstanbietercode direkt in Visual Studio Code erkennt:

JFrog SAST erkennt die Schwachstelle und zeigt ihre Details zusammen mit Data Trace Evidence an (Klicken Sie auf das Bild, um es zu vergrößern)

JFrog SAST hebt die Schwachstelle im Code zusammen mit den entsprechenden Details hervor und liefert „Data Trace Evidence“, die den Datenfluss vom vom Benutzer eingegebenen Feld „req.body“ bis zu dessen Verwendung als Eingabe für die gefährliche „open“-Funktion veranschaulicht.

Um auch bei anderen Angriffen und Zero-Day-Schwachstellen auf dem Laufenden zu bleiben, besuchen Sie das JFrog Security Research Center, um die neuesten Informationen zu CVEs, Schwachstellen und Korrekturen zu erhalten.