CVE-2025-6514

MCP-Remote Control: From AI Agent to RCE via CVE-2025-6514

Alon Barad
Alon Barad
Software Engineer

Jan 10, 2026ยท5 min read

Executive Summary (TL;DR)

If you use `mcp-remote` (v0.0.5 - 0.1.15) to connect your LLM or app to an untrusted MCP server, you are vulnerable to RCE. A malicious server can return a poisoned URL containing shell commands in the credentials field (e.g., `user$(calc)`), which the client blindly executes. Update to v0.1.16 immediately.

The `mcp-remote` package, a client for the Model Context Protocol, suffers from a critical command injection vulnerability. By failing to sanitize username and password fields in returned authorization URLs, the package allows malicious servers to execute arbitrary shell commands on the client machine.

The Hook: When Agents Talk to Strangers

The Model Context Protocol (MCP) is the new shiny object in the AI world. It's the standard way to connect Large Language Models (LLMs) to external data sources and tools. Naturally, developers need a way to connect to these sources remotely, and that's where mcp-remote comes in. It acts as the client, shaking hands with remote servers to fetch context for your AI.

But here is the problem with "shaking hands" on the internet: sometimes the other person has a joy buzzer hidden in their palm that executes rm -rf /. The core issue here is trust. When mcp-remote connects to a server, it expects that server to behave. Specifically, when the server says, "Hey, authenticate over here at this URL," the client dutifully obeys.

This vulnerability exploits that obedience. It turns a standard authentication redirect into a full-blown Remote Code Execution (RCE) scenario, all because the developer assumed that a URL is just a URL, and not a vehicle for shell commands.

The Flaw: Selective Sanitization

The root cause of CVE-2025-6514 is a classic case of "Sanitization Theater." The developers knew that URLs could be dangerous. They implemented a function called sanitizeUrl in src/lib/utils.ts. They clearly spent time on it.

They scrubbed the pathname. They scrubbed the searchParams. They even scrubbed the hash. They were meticulous about ensuring that if you put weird characters in the path, they got encoded. It's like building a fortress with 10-foot steel walls but leaving the front gate unlocked and wide open.

The one part of the URL they didn't touch? The User Information subcomponents: username and password. In the URL specification (and the Node.js URL object), you can embed credentials like this: http://user:pass@domain.com.

Because these fields were left raw and unescaped, and because this URL is eventually passed to a context that invokes a shell (likely via a subprocess call to handle the auth flow), any shell metacharacters placed in the username or password get executed by the operating system. It is a textbook command injection via untrusted input.

The Code: The Smoking Gun

Let's look at the fix to understand the break. The patch (Commit 607b226) is embarrassingly simple, highlighting just how glaring the omission was. The developer had to manually go back and ensure the credentials were encoded before processing the URL.

Here is the logic flow. Notice the new additions marked with +:

export function sanitizeUrl(raw: string) {
   // ... existing checks ...
   if (url.hostname !== encodeURIComponent(url.hostname)) abort()
 
   // The fix: Forcibly sanitise the previously ignored pieces
+  if (url.username) url.username = encodeURIComponent(url.username)
+  if (url.password) url.password = encodeURIComponent(url.password)
   
   // The existing code that gave a false sense of security
   url.pathname = url.pathname.slice(0, 1) + encodeURIComponent(url.pathname.slice(1)).replace(/%2f/ig,'/')
   url.search = url.search.slice(0, 1) + Array.from(url.searchParams.entries()).map(sanitizeParam).join('&')
   url.hash = url.hash.slice(0, 1) + encodeURIComponent(url.hash.slice(1))
}

Before those two lines were added, url.username was passed through verbatim. If url.username was $(calc), the downstream system saw a command substitution request rather than a username.

The Exploit: Popping Shells via Auth

Exploiting this requires a bit of social engineering or a supply chain attack configuration, but technical execution is trivial. The attacker sets up a malicious MCP server. When the victim (the mcp-remote client) connects, the flow looks like this:

  1. Victim: Connects to mcp://evil-server.com.
  2. Attacker: Returns a response indicating that authentication is required at a specific endpoint.
  3. The Payload: The attacker crafts the authorization_endpoint URL to include the payload in the credentials.

Payload Example: http://user$(calc):password@attacker.com

When mcp-remote processes this URL to handle the authentication step, the presence of $(...) (command substitution) or backticks triggers execution. If you are on Windows, calc.exe pops up. If you are on Linux, $(whoami) runs.

Here is the attack flow visualization:

The Impact: Why This Matters

This is rated Critical (9.6) for a reason. It requires no authentication to exploit (the attacker is the one doing the authenticating) and results in total compromise of the host application's environment.

Consider the context: mcp-remote is often used by AI Agents. These agents often have access to sensitive keys, databases, and internal APIs. If an attacker gains RCE on the agent's host, they don't just get the server; they get everything the AI has access to.

Since this runs on the client side (the machine initiating the connection), it can breach firewalls. An internal developer connecting to an external "cool new tool" MCP server could inadvertently bridge their internal network to the attacker.

The Fix: Remediation

The fix is available in version 0.1.16. If you are running anything from 0.0.5 to 0.1.15, you are vulnerable.

Immediate Steps:

  1. Update: npm install mcp-remote@latest.
  2. Audit: Check your logs for connection attempts to unknown or suspicious domains.
  3. Restrict: If you are building tools on top of MCP, ensure your agents are not allowed to connect to arbitrary user-defined URLs without a whitelist.

The patch simply ensures that encodeURIComponent is applied to the username and password, neutralizing the special characters required for shell injection.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.6/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
EPSS Probability
0.73%
Top 28% most exploited

Affected Systems

Node.js applications using mcp-remoteAI/LLM Agents integrating Model Context ProtocolDeveloper tools connecting to remote MCP servers

Affected Versions Detail

Product
Affected Versions
Fixed Version
mcp-remote
geelen
>= 0.0.5 < 0.1.160.1.16
AttributeDetail
CWE IDCWE-78 (OS Command Injection)
CVSS Score9.6 (Critical)
Attack VectorNetwork
EPSS Score0.00727 (72nd Percentile)
Exploit StatusPoC Available
PatchCommit 607b226
CWE-78
OS Command Injection

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

Vulnerability Timeline

Fix commit pushed to GitHub
2025-06-17
Vulnerability disclosed by JFrog Security Research
2025-07-09
CVE-2025-6514 Published
2025-07-09

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.