PoC Archive PoC Archive
Critical CVE-2025-11953 patched

React Native Community CLI Metro Dev Server `/open-url` OS Command Injection (CVE-2025-11953)

by boroeurnprach · 2026-07-06

CVSS 9.8/10
Severity
Critical
CVE
CVE-2025-11953
Category
network
Affected product
@react-native-community/cli / @react-native-community/cli-server-api (Metro Development Server, openURLMiddleware)
Affected versions
@react-native-community/cli 4.8.0 – 20.0.0-alpha.2 (fixed in 20.0.0)
Disclosed
2026-07-06
Patch status
patched

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07-06
Author / Researcherboroeurnprach
CVE / AdvisoryCVE-2025-11953
Categorynetwork
SeverityCritical
CVSS Score9.8 (per NVD)
StatusWeaponized
Tagsreact-native, metro, dev-server, cli-server-api, open-url, command-injection, cwe-78, unauthenticated, node.js, python, windows, cross-platform
RelatedN/A

Affected Target

FieldValue
Software / System@react-native-community/cli / @react-native-community/cli-server-api (Metro Development Server, openURLMiddleware)
Versions Affected@react-native-community/cli 4.8.0 – 20.0.0-alpha.2 (fixed in 20.0.0)
Language / PlatformJavaScript/Node.js (Metro dev server, target); Python 3 (PoC exploit client)
Authentication RequiredNo
Network Access RequiredYes

Summary

The Metro Development Server started by the React Native Community CLI binds to external network interfaces by default and exposes an /open-url HTTP endpoint (implemented by openURLMiddleware in @react-native-community/cli-server-api) that is intended to open a URL/deep-link in a browser or simulator during development. The root cause is that the url field from the client’s JSON POST body is passed through to the OS’s “open” mechanism without validating that it is actually a URL or sanitizing it against shell metacharacters, so the value is effectively executed as an OS command/executable. Because the dev server requires no authentication and listens on all interfaces, any network-adjacent attacker who can reach port 8081 can submit a crafted url value to have the developer’s machine execute arbitrary programs — on Windows this is demonstrated by submitting calc.exe directly as the “URL”, launching Calculator, and the same code path extends to arbitrary shell command injection.


Vulnerability Details

Root Cause

The vulnerable code path is the openURLMiddleware handler shipped in @react-native-community/cli-server-api, mounted by the CLI’s Metro dev server at POST /open-url. The repro server in this PoC mounts the real, unmodified vendored middleware directly:

1
2
3
const openURLMiddleware = require('@react-native-community/cli-server-api/build/openURLMiddleware').default;
const app = connect();
app.use('/open-url', openURLMiddleware);

openURLMiddleware reads the url field from the POST body and passes it to the platform “open” mechanism (e.g. start/cmd.exe on Windows, open/xdg-open via a shell on macOS/Linux) without validating that the value is a well-formed http(s):// URL or an app deep-link, and without shell-escaping it. Since the underlying open call is executed through a shell/command interpreter, the attacker-controlled string is interpreted as a command rather than strictly as a URL argument — on Windows, simply supplying an executable name (calc.exe) as the “URL” is enough to have it launched directly, and the same unsanitized-argument path allows appending arbitrary shell metacharacters for full OS command injection.

Attack Vector

  1. A developer runs the React Native CLI (npx react-native start or equivalent), which starts the Metro dev server on port 8081, bound to all interfaces by default (not just localhost).
  2. An unauthenticated attacker on the same network (or with any network path to port 8081) sends a POST /open-url request with a JSON body {"url": "<attacker-controlled string>"}.
  3. The server’s openURLMiddleware passes the string straight through to the OS-level “open” call with no validation/sanitization.
  4. On Windows, the string is executed as a command/program directly (PoC demonstrates calc.exe launching Calculator); on POSIX platforms the same code path is reachable via the shell used to invoke open/xdg-open, allowing command injection via shell metacharacters in the supplied value.
  5. The command/executable runs with the privileges of the developer’s Metro/Node process — i.e. the developer’s own user account.

Impact

  • Unauthenticated remote code execution on any developer machine (or CI runner) with a Metro dev server reachable over the network.
  • Full compromise of the developer’s workstation, including access to source code, credentials, SSH/cloud keys, and further lateral movement from the development environment.
  • Trivial to weaponize since the endpoint requires zero authentication and the dev server is commonly left running and network-accessible during normal React Native development workflows.

Environment / Lab Setup

Target:   Node.js environment with @react-native-community/cli / cli-server-api 4.8.0 - 20.0.0-alpha.2
          (repro server: vuln_server.js, mounts the real openURLMiddleware on a bare connect() app, port 8081)
Attacker: Python 3 with `requests` (poc.py for POSIX targets, windows_PoC.py for Windows targets)
Install:  npm install   (pulls @react-native-community/cli, cli-server-api, metro, connect, etc. per package.json)

Proof of Concept

PoC Script

See vuln_server.js, poc.py, and windows_PoC.py in this folder.

1
2
3
4
5
6
7
npm install

node vuln_server.js

python3 poc.py

python3 windows_PoC.py

Underlying request made by both PoC scripts against TARGET:8081:

1
2
3
curl -X POST http://TARGET:8081/open-url \
  -H 'Content-Type: application/json' \
  -d '{"url": "calc.exe"}'

Detection & Indicators of Compromise

Signs of compromise:

  • Metro dev server (node/react-native) process tree showing unexpected child processes it did not itself need to launch
  • /open-url requests in dev-server logs originating from non-loopback source IPs
  • Unexpected outbound network connections or file modifications originating from a developer workstation running react-native start

Remediation

ActionDetail
Primary fixUpgrade @react-native-community/cli (and cli-server-api) to version 20.0.0 or later, which fixes the open-url command injection
Interim mitigationBind the Metro dev server to localhost only (avoid --host 0.0.0.0/exposing port 8081 externally), block port 8081 at the host/network firewall for untrusted networks, and avoid running the dev server on shared/untrusted Wi-Fi

References


Notes

Mirrored from https://github.com/boroeurnprach/CVE-2025-11953-PoC on 2026-07-06. Note the source repository has since been transferred to a different owner on GitHub; contents mirrored here (vuln_server.js, poc.py, windows_PoC.py, plus supporting package.json/metro.config.js) reflect the state at time of cloning and implement the real React Native Metro dev-server /open-url command-injection PoC as described in the prior-pass verification note.

poc.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import requests
import json

url = "http://localhost:8081/open-url"

#Attempt to open google web page
payload = {"url": "https://google.com"}

print(f"Sending payload to {url}...")
try:
    response = requests.post(url, json=payload)
    print(f"Status Code: {response.status_code}")
    print("If successful, a browser should open on your screen.")
except Exception as e:
    print(f"Error: {e}")