flawopen.com/Incidents/microsoft-autogen-studio-autojack-agent-rce-june-2026
AutoJack:AutoGen StudioのRCE研究チェーン
MicrosoftはAutoGen Studio開発ビルドでローカルMCP経由のRCE研究連鎖を報告。公開前に修正され、PyPIには含まれませんでした。[1], [2]
訪問者のメモを読むアシスタントがいる研究室を想像してください。同じ建物で働いているため、アシスタントは制御室に入れます。受付はその部屋だけ通常の本人確認を省き、訪問者のメモが起動する道具まで選べます。AutoJack はこの抜け道をつなぎました。ブラウジングエージェントが信頼できない内容を表示し、それが AutoGen Studio のローカル MCP 制御面に届き、呼び出し側の設定から子プロセスが起動し得ます。制御面を認証し、起動設定を WebSocket URL から外し、開発者の端末からエージェントを隔離してください。Microsoft は PyPI リリース前に upstream ブランチで修正したと説明しています。この調査対象はリリース前のソースコードであり、顧客向けの影響バージョンではありません。[1]
主要な概念と専門用語
AutoJack- AutoGen Studio の開発版で実証された攻撃連鎖に Microsoft が付けた名称です。報告によると、該当する MCP ルートは PyPI 公開パッケージに入る前に修正されました。[1], [2]
Loopback / localhost- 同じホスト上のサービスへ接続するネットワークインターフェースです。外部ネットワークからの到達を制限しますが、エージェントのブラウザーなどのローカルプロセスを同一ホストのサービスから隔離するものではありません。[1]
WebSocket Origin 検証- WebSocket ハンドシェイク時にサーバーが Origin ヘッダーを確認する制御です。Microsoft のテストでは、この検証だけではエージェントが表示したページからローカル MCP サービスへの到達を防げませんでした。[1], [3]
StdioServerParams- AutoGen の MCP stdio 設定で、サーバープロセスの起動に使うプログラムと引数を含みます。報告されたルートは WebSocket URL のパラメーターからこれをデコードしていました。[1], [2], [5]
Confused deputy- 特権を持つコンポーネントに、自身の権限を信頼できない相手のために使わせる状態です。ここではブラウジングエージェントとローカル Studio が、ページ内容からホスト上のプロセス起動へつながる経路になりました。[1]
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-78 CWE-306 CWE-1385
- OWASP GenAI 2025 — LLM06:2025 過剰なエージェンシー
インシデントのタイムライン
Microsoft が AutoJack の調査結果と緩和策を公開しました。説明されているのは制御されたローカル実証であり、顧客への攻撃が確認されたという報告ではありません。[1]
ステップ・バイ・ステップの攻撃フロー
エージェントが信頼できないコンテンツを閲覧する
プロンプト、取得したページ、または別の信頼できない入力により、AutoGen のブラウジングエージェントが AutoGen Studio と同じ端末上で攻撃者のページを表示します。[1]
ソースコード比較:脆弱 vs 堅牢化
脆弱な例は呼び出し側が制御する起動設定を解析しますが、意図的にプロセスは起動しません。upstream の修正では、設定を WebSocket クエリから外してサーバー側の一回限りのセッション状態に保存し、MCP ルートの認証も再び有効にしました。この防御パターンでは、有効期限、サイズ上限、並行アクセス用ロック、実行ファイル許可リストも加えています。例の名前は承認済み MCP バイナリに置き換えてください。Origin 検証や OS 隔離の代わりにはなりません。[1], [2]
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:
# 開発用ルートは URL で渡された設定を信頼していた。
encoded = query["server_params"]
decoded = base64.b64decode(encoded)
return StdioServerParams(**json.loads(decoded))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 は検証済みミドルウェアから取得し、リクエストデータから取得してはならない。
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]エンジニアリング&システム堅牢化チェックリスト
- ✓WebSocket ハンドシェイクを含む MCP 制御面のすべてのルートで認証と認可を必須にし、各認証モードをそのルートでテストする。[1], [2], [4]
- ✓WebSocket URL やブラウザーから制御できる入力でプロセス起動設定を受け取らない。認証済みリクエストで登録し、短時間・一回限りのサーバー側セッションに結び付ける。[2]
- ✓保留中のセッションを期限切れにし、件数を制限し、各セッションを原子的に消費して、未知・期限切れ・再利用された ID を拒否する。[2]
- ✓許可した MCP サーバープログラムと固定の引数スキーマのみを実行し、モデル出力やページ内容から任意のプログラムを選ばせない。[1], [5]
- ✓信頼できないページを表示するブラウジングエージェントを、特権を持つローカル制御面から隔離する。可能であればブラウザーサンドボックスから loopback への接続を遮断する。[1], [3]
- ✓エージェントと Studio を最小権限の ID または使い捨て VM/コンテナーで実行し、エージェントランタイムが予期しない子プロセスを作成したら通知する。[1]
- ✓AutoGen Studio をソースからビルドする場合は b047730 以降を使う。Microsoft は当時の PyPI パッケージに該当ルートが含まれないとしています。実際に配備する成果物を確認してください。[1], [2]