FlawOpen

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

● Microsoft AutoGen project · CWE-1385 / CWE-306 / CWE-78 · 2026-06-18 · Haute
Recherche · FlawOpen

AutoJack : chaîne de RCE de recherche dans AutoGen Studio

Microsoft a trouvé une chaîne RCE locale via MCP dans le build de développement d’AutoGen Studio. Corrigée avant sortie, absente de PyPI.[1], [2]

💡 Explication en Langage Simple (ELI5)

Imaginez un laboratoire où un assistant lit les notes de visiteurs. Il peut entrer dans la salle de contrôle parce qu’il travaille dans le même bâtiment. L’accueil saute la vérification d’identité habituelle pour cette salle, et une note de visiteur peut choisir l’outil lancé. AutoJack a combiné ces raccourcis : un agent de navigation affichait du contenu non fiable, ce contenu atteignait le plan de contrôle MCP local d’AutoGen Studio et les paramètres fournis par l’appelant pouvaient entraîner le lancement d’un processus enfant. Il faut authentifier la salle de contrôle, retirer les paramètres de lancement des URL WebSocket et isoler l’assistant du poste du développeur. Microsoft indique que le correctif est arrivé sur la branche upstream avant toute version PyPI. La recherche porte sur du code prépublication, pas sur une version client concernée.[1]

Concepts Clés et Termes

AutoJack
Nom donné par Microsoft à une chaîne démontrée dans une version de développement d’AutoGen Studio. Le rapport précise que la route MCP concernée a été corrigée avant son inclusion dans un paquet PyPI publié.[1], [2]
Loopback / localhost
Interface réseau permettant d’atteindre des services sur le même hôte. Elle limite l’accès depuis le réseau externe, mais n’isole pas les processus locaux, comme le navigateur d’un agent, des autres services de l’hôte.[1]
Validation Origin du WebSocket
Vérification côté serveur de l’en-tête Origin lors du handshake WebSocket. Microsoft a constaté que, dans le flux d’agent testé, cette vérification seule n’empêchait pas la page affichée d’atteindre le service MCP local.[1], [3]
StdioServerParams
Configuration stdio MCP d’AutoGen, comprenant le programme et les arguments qui lancent un processus serveur. La route décrite décodait ces paramètres depuis une valeur de l’URL WebSocket.[1], [2], [5]
Confused deputy
Un composant privilégié est amené à utiliser ses propres droits pour une partie non fiable. Ici, l’agent de navigation et le service local Studio reliaient le contenu d’une page au lancement de processus sur l’hôte.[1]

Correspondances de référentiels

Chronologie de l'Incident

Avant le 18/06/2026

Microsoft a signalé le comportement au MSRC ; les mainteneurs d’AutoGen ont renforcé la branche upstream dans le commit b047730. Le rapport indique que la route WebSocket MCP concernée n’était pas présente dans un paquet PyPI publié.[1], [2]

18/06/2026

Microsoft a publié la recherche AutoJack et ses mesures de protection. Elle décrit une démonstration locale contrôlée, pas une attaque confirmée contre des clients.[1]

Déroulement de l'Attaque Étape par Étape

Étape 1

L’agent doit consulter du contenu non fiable

Un prompt, une page récupérée ou une autre entrée non fiable amène un agent de navigation AutoGen à afficher une page contrôlée par un attaquant sur le poste qui exécute AutoGen Studio.[1]

Étape 2

La page affichée atteint le WebSocket MCP local

Dans la configuration testée par Microsoft, le script de la page pouvait se connecter au point de terminaison MCP de Studio sur localhost. La liste Origin ne séparait pas le plan de contrôle du code affiché par le navigateur local de l’agent.[1], [3]

Étape 3

La route MCP contourne le middleware d’authentification

Le middleware concerné ignorait les chemins /api/mcp, en supposant que le gestionnaire WebSocket ferait lui-même l’authentification. Microsoft indique que ce contrôle manquait.[1], [4]

Étape 4

La requête WebSocket fournit les paramètres serveur

La route de développement décodait server_params depuis l’URL vers StdioServerParams. Le transport stdio MCP utilisait ensuite command et args pour démarrer un processus enfant.[1], [2], [5]

Étape 5

La chaîne atteint le compte de l’hôte

Le processus Studio local démarre le processus enfant configuré avec les privilèges du système d’exploitation. Microsoft l’a démontré dans une preuve de concept locale contrôlée et précise que la route n’était pas dans un paquet PyPI publié.[1], [2]

Code Source : Vulnérable vs Sécurisé

L’exemple vulnérable analyse des paramètres de lancement contrôlés par l’appelant, mais ne démarre volontairement aucun processus. Le correctif upstream a retiré ces paramètres de la requête WebSocket, les a placés dans une session serveur à usage unique et a rétabli l’authentification des routes MCP. Ce modèle défensif ajoute expiration, limite de taille, verrouillage concurrent et liste d’exécutables autorisés ; remplacez le nom d’exemple par les binaires MCP approuvés. Il ne remplace ni la validation Origin ni l’isolation du système d’exploitation.[1], [2]

IMPLÉMENTATION VULNÉRABLE
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 route de développement faisait confiance à la configuration fournie dans l’URL.
    encoded = query["server_params"]
    decoded = base64.b64decode(encoded)
    return StdioServerParams(**json.loads(decoded))
PATCH SÉCURISÉ ET ROBUSTE
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 doit provenir d’un middleware vérifié, jamais des données de la requête.
    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]

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources