FlawOpen

flawopen.com/Incidents/microsoft-autogen-studio-autojack-agent-rce-june-2026

● Microsoft AutoGen project · CWE-1385 / CWE-306 / CWE-78 · 2026-06-18 · Alta
Investigación · FlawOpen

AutoJack: cadena de RCE de investigación en AutoGen Studio

Microsoft halló RCE local vía MCP en un build de desarrollo de AutoGen Studio. Se corrigió antes del lanzamiento y no llegó a PyPI.[1], [2]

💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina un laboratorio donde un asistente lee notas de visitantes. El asistente puede entrar en la sala de control porque trabaja en el mismo edificio. La recepción omite la comprobación de identidad habitual para esa sala, y una nota de un visitante puede elegir qué herramienta se inicia. AutoJack encadenó esos atajos: un agente de navegación mostró contenido no confiable, ese contenido llegó al plano de control MCP local de AutoGen Studio y la configuración aportada por quien llamaba podía provocar un proceso hijo. La lección es autenticar la sala de control, sacar la configuración de inicio de las URL de WebSocket y aislar al asistente del equipo del desarrollador. Microsoft afirma que se corrigió en la rama upstream antes de una versión de PyPI. La investigación trata de código previo al lanzamiento, no de una versión de clientes afectada.[1]

Conceptos Clave y Términos

AutoJack
Nombre de Microsoft para una cadena demostrada en una compilación de desarrollo de AutoGen Studio. El informe dice que la ruta MCP afectada se corrigió antes de aparecer en un paquete PyPI publicado.[1], [2]
Loopback / localhost
Interfaz de red que accede a servicios del mismo host. Limita el acceso desde redes externas, pero no aísla procesos locales, como el navegador de un agente, de otros servicios del host.[1]
Validación de Origin en WebSocket
Comprobación del servidor sobre el encabezado Origin durante el handshake de WebSocket. Microsoft observó que, en el flujo de agente probado, esa comprobación por sí sola no impedía que la página mostrada llegara al servicio MCP local.[1], [3]
StdioServerParams
Configuración stdio de MCP en AutoGen, incluido el programa y sus argumentos para iniciar un proceso servidor. La ruta descrita decodificaba estos datos desde un parámetro de la URL WebSocket.[1], [2], [5]
Confused deputy
Un componente privilegiado es inducido a usar su propia autoridad en beneficio de una parte no confiable. Aquí, el agente de navegación y el servicio local de Studio conectaban el contenido web con la creación de procesos en el host.[1]

Mapeos de marcos

Cronología del Incidente

Antes del 18/06/2026

Microsoft comunicó el comportamiento al MSRC; los mantenedores de AutoGen reforzaron la rama upstream en el commit b047730. El informe dice que la ruta MCP WebSocket afectada aún no se había publicado en PyPI.[1], [2]

18/06/2026

Microsoft publicó la investigación AutoJack y sus medidas de mitigación. Describe una demostración local controlada, no un ataque confirmado contra clientes.[1]

Flujo de Ataque Paso a Paso

Paso 1

Se pide al agente que navegue por contenido no confiable

Un prompt, una página recuperada u otra entrada no confiable hace que un agente de navegación de AutoGen muestre una página controlada por un atacante en el mismo equipo que ejecuta AutoGen Studio.[1]

Paso 2

La página mostrada alcanza el WebSocket MCP local

En la configuración probada por Microsoft, el script de la página podía conectarse al endpoint MCP de Studio en localhost. La lista de Origin no aislaba el plano de control del código mostrado por el navegador local del agente.[1], [3]

Paso 3

La ruta MCP omite el middleware normal de autenticación

El middleware afectado omitía las rutas /api/mcp porque se suponía que el handler WebSocket las autenticaría. Microsoft indica que el handler no hacía esa comprobación.[1], [4]

Paso 4

La consulta WebSocket controla los parámetros del servidor

La ruta de desarrollo decodificaba server_params de la URL a StdioServerParams. Después, el transporte stdio de MCP usaba command y args para iniciar un proceso hijo.[1], [2], [5]

Paso 5

La cadena llega a la cuenta del host

El proceso local de Studio inicia el proceso hijo configurado con los privilegios del sistema operativo. Microsoft lo mostró en una prueba de concepto local controlada y afirma que la ruta no estaba en un paquete PyPI publicado.[1], [2]

Código Fuente: Vulnerable vs. Seguro

El ejemplo vulnerable analiza parámetros de inicio controlados por quien llama, pero deliberadamente no inicia procesos. La corrección upstream sacó esos parámetros de la consulta WebSocket, los guardó en una sesión server-side de un solo uso y restauró la autenticación en las rutas MCP. Este patrón defensivo añade caducidad, límite de tamaño, bloqueo concurrente y una lista permitida de ejecutables; sustituya el nombre de ejemplo por los binarios MCP aprobados. No sustituye la validación de Origin ni el aislamiento del sistema operativo.[1], [2]

IMPLEMENTACIÓN VULNERABLE
import base64
import json
from dataclasses import dataclass


@dataclass(frozen=True)
class StdioServerParams:
    command: str
    args: tuple[str, ...]


def params_from_websocket_query(query: dict[str, str]) -> StdioServerParams:
    # La ruta de desarrollo confiaba en la configuración proporcionada en la URL.
    encoded = query["server_params"]
    decoded = base64.b64decode(encoded)
    return StdioServerParams(**json.loads(decoded))
PARCHE SEGURO Y ROBUSTO
from dataclasses import dataclass
from threading import Lock
from time import monotonic
from uuid import uuid4


@dataclass(frozen=True)
class StdioServerParams:
    command: str
    args: tuple[str, ...]


SESSION_TTL = 30.0
MAX_PENDING = 256
ALLOWED_COMMANDS = {"approved-mcp-server"}
pending: dict[str, tuple[float, StdioServerParams]] = {}
pending_lock = Lock()


def register_params(params: StdioServerParams, authenticated: bool) -> str:
    # authenticated debe proceder de middleware verificado, nunca de los datos de la solicitud.
    if not authenticated:
        raise PermissionError("MCP control-plane authentication required")
    if params.command not in ALLOWED_COMMANDS:
        raise ValueError("unapproved MCP server executable")
    now = monotonic()
    with pending_lock:
        for key, (expires, _) in list(pending.items()):
            if expires <= now:
                pending.pop(key, None)
        if len(pending) >= MAX_PENDING:
            raise RuntimeError("pending MCP session limit reached")
        session_id = str(uuid4())
        pending[session_id] = (now + SESSION_TTL, params)
        return session_id


def consume_params(session_id: str, authenticated: bool) -> StdioServerParams:
    if not authenticated:
        raise PermissionError("MCP control-plane authentication required")
    with pending_lock:
        entry = pending.pop(session_id, None)
    if entry is None or entry[0] <= monotonic():
        raise ValueError("unknown or expired MCP session")
    return entry[1]

Lista de Verificación de Seguridad para Ingeniería

Fuentes