CyberXtron
Metro Meltdown: CyberXTron Confirms Unauthenticated RCE via React Native CLI Command Injection (CVE-2025-11953)
RCECyberSecurityCommandInjectionOSCommandInjectionSupplyChainSecurityCVE202511953MetroServer

Metro Meltdown: CyberXTron Confirms Unauthenticated RCE via React Native CLI Command Injection (CVE-2025-11953)

Executive Summary:

CVE-2025-11953 is a critical remote code execution (RCE) vulnerability in the @react-native-community/cli-server-api package (versions 4.8.0 through 20.0.0-alpha.2), which is bundled with the React Native CLI. This flaw enables unauthenticated attackers to execute arbitrary operating system commands on Windows hosts through the exposed /open-url endpoint on the Metro development server. No authentication or user interaction is required, rendering the vulnerability exploitable over the network. The issue has been replicated in a controlled lab environment using React Native 0.80.0, demonstrating full RCE by launching calc.exe and writing files—including a proof-of-concept payload that creates C:\temp\pwned.txt containing the string "pwned." Immediate upgrades to CLI version 20.0.0 or later are strongly recommended.

Vulnerability Overview:

Attribute

Details

CVE ID

CVE-2025-11953

Severity

Critical

CVSS Score

9.8 (Critical)

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Affected Component

@react-native-community/cli-server-api

Vulnerable Versions

4.8.0 – 20.0.0-alpha.2

Patched Versions

20.0.0 and above (including patches in 17.0.1, 18.0.1, 19.1.2)

Attack Vector

Network (Remote)

Authentication Required

None

User Interaction

None

Disclosure Date

November 3, 2025

Vulnerability Details:

CVE-2025-11953 targets the @react-native-community/cli-server-api package, a cornerstone of React Native's tooling suite responsible for bundling, hot-reloading, and dev server orchestration. The vulnerability manifests as an unauthenticated RCE when the Metro server processes POST requests to /open-url, a feature intended for opening debug links in the system browser.

At its core, the issue arises from insufficient input validation: The endpoint accepts a JSON payload with a url parameter and feeds it directly into the open npm package, which spawns shell commands without sanitization. This creates a textbook command injection sink, particularly potent on Windows due to cmd.exe invocation patterns.

Affected components include:

  • Package: @react-native-community/cli-server-api versions 4.8.0 through 20.0.0-alpha.2
  • Bundled In: React Native CLI v4.8.0–19.x (e.g., React Native 0.72–0.80)
  • Platforms: Primarily Windows (full RCE); macOS/Linux (limited to URI schemes or file execution)

The flaw's elegance lies in its stealth: No errors surface in server logs, and the response is a benign "OK," masking the executed payload.

Root Cause:

The root cause is a failure to enforce input sanitization and access controls in the /open-url endpoint handler. Specifically:

  • Missing Input Validation Guard: The openURLMiddleware extracts the 'url' parameter from POST requests without URI parsing, regex whitelisting, or scheme enforcement, allowing arbitrary command strings to bypass intended browser-opening logic.
  • Unsafe Shell Propagation: Invocation of the 'open' package forwards unescaped user input directly to platform-specific subprocess spawners, particularly vulnerable on Windows where cmd.exe interpretation enables nested command execution without isolation.
  • Implicit Network Exposure: Metro's configuration defaults to binding on all interfaces (0.0.0.0:8081) despite logging a localhost-only facade, eliminating any inherent access barrier and assuming trusted local origins.
  • Absence of Audit and Feedback Mechanisms: The handler provides no logging of executed payloads or anomalous inputs, coupled with generic success responses, which conceals exploitation and erodes forensic traceability in dev environments.

Exploitation Flow:

Exposure & Risk

This vulnerability casts a wide net across the React Native landscape, which boasts ~2 million weekly CLI downloads and underpins apps from Meta's ecosystem to indie startups. Exposure stems from dev habits: Metro servers often spin up on laptops in coffee shops (WiFi pivots) or cloud VMs (public IPs), with port 8081 firewalled sporadically.

Risk Breakdown:

  • Likelihood: High – Default configs expose; no auth barrier.
  • Impact: Catastrophic for devs – RCE yields source code theft, credential harvest (e.g., npm tokens), or malware implantation. In CI/CD, it poisons builds.
  • Attack Surface: ~500K exposed instances per recent scans; Windows devs (60% share) face worst-case.
  • Business Ramifications: Supply-chain ripple effects, akin to SolarWinds, eroding trust in RN tooling.

Blind exploitation (no feedback needed) suits wormable scenarios, while the "localhost" log lulls users into false security.

POC & Active Exploitation: CVE-2025-11953:

This proof-of-concept demonstrates unauthenticated remote code execution (RCE) in the React Native Community CLI's Metro development server. The flaw affects developers running vulnerable versions during app bundling and testing, allowing attackers to inject and execute arbitrary OS commands on the host machine without any form of authentication or session validation. On Windows hosts, this leads to full system compromise, such as spawning processes, file creation, or data exfiltration.

The core issue stems from the @react-native-community/cli-server-api package's /open-url endpoint, which is designed to open external URLs in the default browser for debugging purposes. However, it blindly trusts user-supplied input and forwards it to the underlying open npm package, which invokes system shell commands without proper escaping or validation.

Endpoint Exposure and Lack of Authentication 

The Metro server, powered by the React Native CLI, exposes HTTP endpoints including /open-url over the network by default. When initiated via npx react-native start, the server logs a binding to localhost:8081 but actually listens on 0.0.0.0:8081 (all interfaces) due to an undefined host parameter in the Metro configuration:

// Simplified from metro/src/Server.js (vulnerable versions)
const hostname = args.host || 'localhost'; // args.host is undefined
console.log(`Starting dev server on http://${hostname}:8081`);
// But in runServer:
await runServer(metroConfig, { host: args.host }); // Defaults to 0.0.0.0

This misconfiguration makes the endpoint publicly accessible from any network peer, with no middleware enforcing authentication, API keys, or even basic origin checks. Requests to /open-url are processed immediately, assuming they originate from a legitimate dev tool integration—a dangerous oversight in a tool meant for local development.

In the handler for this endpoint (openURLMiddleware in cli-server-api/src/openURLMiddleware.ts), the code parses the incoming POST body without verifying the requester:

// Vulnerable code snippet from @react-native-community/cli-server-api
export default function openURLMiddleware(
  req: IncomingMessage,
  res: ServerResponse,
  next: () => void,
) {
  if (req.method === 'POST' && req.url === '/open-url') {
    const { url } = parseBody(req) as { url: string }; // No validation on 'url'
    open(url).catch(next); // Directly executes via 'open' package
    res.end('OK');
    return;
  }
  next();
}

Here, the url field from the JSON payload is extracted and passed verbatim to open(url). There's no check for session tokens, IP whitelisting, or even if the url resembles a valid URI. Unauthenticated actors can simply POST to the endpoint, bypassing any dev environment safeguards.

Command Injection in the open Package

The open package (v6.4.0 and below, bundled in vulnerable CLI versions) handles URL opening by spawning platform-specific subprocesses. On Windows, it relies on cmd.exe with the start command, creating an ideal vector for injection:

// From open package's windows.js (simplified)
const cmd = process.platform === 'win32'
  ? `cmd /c start "" /b "${escapedTarget}"` // Target is user-controlled 'url'
  : ...;

child_process.spawn(cmd, { shell: true }); // Shell interpretation enables chaining

The escapedTarget only handles basic metacharacters like & (escaped to ^&), but fails against command separators like /c or full command strings. Attackers can craft a payload that hijacks the start invocation. For instance:

  • Legitimate: open("https://example.com") → cmd /c start "" /b "https://example.com"
  • Malicious: open("cmd /c echo pwned > C:\temp\pwned.txt")cmd /c start "" /b "cmd /c echo pwned > C:\temp\pwned.txt" → Writes proof file via nested cmd.

This shell-enabled spawn allows arbitrary command execution, as the inner cmd /c interprets the payload independently. The absence of argument isolation or safe URI parsing in the CLI exacerbates the risk, turning a convenience feature into a backdoor.

Furthermore, the handler doesn't log the executed command or audit the url input, leaving no traces for post-incident forensics. In multi-developer setups or CI/CD pipelines running Metro servers, this could enable supply-chain attacks by compromising build agents.

Parameter Manipulation and Payload Structure

The /open-url endpoint expects a minimal JSON structure with a single url string. No additional headers or tokens are required, simplifying automation:

  • url: The injectable string; must evade basic escaping but can chain commands via /c.
    • Example: {"url": "cmd /c whoami > C:\temp\pwned.txt"} for recon.

In a real attack, enumerate open 8081 ports via Shodan or nmap, then iterate payloads. The response's success code confirms blind RCE without feedback loops. For evasion, wrap in valid URL prefixes like file://cmd /c ... to blend with benign traffic.

This POC highlights the severity: Zero-effort exploitation from any network position, with impacts rivaling traditional RCE in web apps. Patch by upgrading CLI to v20.0.0+, which adds url sanitization and host binding defaults.

Mitigation:

•    Upgrade Path: Update to @react-native-community/cli-server-api version 20.0.0 or later. This patch introduces strict URI sanitization to prevent command injection.
•    Runtime Controls: Bind the Metro server to localhost using npx react-native start --host 127.0.0.1, and restrict inbound traffic to port 8081 via firewall rules.
•    Best Practices: Regularly audit package.json dependencies (npm audit, Dependabot), and avoid exposing development servers to the public network or CI/CD runners.
•    Detection: Implement SIEM/EDR rules to detect anomalous /open-url POST requests and monitor for cmd.exe or start processes spawned by node.exe.

Timeline:

Conclusion:

CVE-2025-11953 exposes a stark reminder that even trusted developer tools like React Native's CLI can harbor devastating flaws, transforming routine npm start sessions into unwitting gateways for RCE attacks that compromise codebases, credentials, and build integrity. As our replication underscores, the blend of network exposure and unvalidated command injection demands immediate action—upgrading to patched versions and enforcing localhost bindings—to shield the vibrant RN community from opportunistic threats. 

 

Elevate your security—get curated threat insights in your inbox.

Metro Meltdown: CyberXTron Confirms Unauthenticated RCE via React Native CLI Command Injection (CVE-2025-11953) | CyberXTron Blog