FlawOpen

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

● Microsoft AutoGen project · CWE-1385 / CWE-306 / CWE-78 · 2026-06-18 · Tinggi
Riset Keamanan · FlawOpen

AutoJack: rantai RCE riset di AutoGen Studio

Riset Microsoft menemukan rantai RCE lokal via MCP pada build pengembangan AutoGen Studio. Diperbaiki sebelum rilis; tidak masuk PyPI.[1], [2]

💡 Penjelasan Sederhana (ELI5)

Bayangkan laboratorium riset tempat asisten membaca catatan pengunjung. Asisten boleh masuk ke ruang kendali karena bekerja di gedung yang sama. Resepsionis melewati pemeriksaan identitas biasa untuk ruangan itu, dan catatan pengunjung dapat menentukan alat yang dijalankan. AutoJack merangkai jalan pintas tersebut: agen penjelajah menampilkan konten tak tepercaya, konten itu mencapai control plane MCP lokal AutoGen Studio, lalu pengaturan dari pemanggil dapat menyebabkan proses anak dimulai. Autentikasi ruang kendali, keluarkan pengaturan peluncuran dari URL WebSocket, dan pisahkan asisten dari workstation pengembang. Microsoft mengatakan masalah ini diperbaiki di branch upstream sebelum rilis PyPI. Laporan ini membahas kode sumber pra-rilis, bukan rilis pelanggan yang terdampak.[1]

Konsep Kunci & Istilah

AutoJack
Nama Microsoft untuk rantai yang didemonstrasikan pada build pengembangan AutoGen Studio. Laporan menyebut rute MCP terdampak diperbaiki sebelum masuk ke paket PyPI yang dirilis.[1], [2]
Loopback / localhost
Antarmuka jaringan untuk mengakses layanan pada host yang sama. Ini membatasi akses dari jaringan luar, tetapi tidak mengisolasi proses lokal seperti browser agen dari layanan lain pada host tersebut.[1]
Validasi Origin WebSocket
Pemeriksaan server terhadap header Origin saat handshake WebSocket. Microsoft mendapati pemeriksaan ini saja tidak mencegah halaman yang ditampilkan dalam alur agen yang diuji mencapai layanan MCP lokal.[1], [3]
StdioServerParams
Konfigurasi stdio MCP AutoGen yang berisi program dan argumen untuk memulai proses server. Rute yang dilaporkan mendekode pengaturan ini dari parameter URL WebSocket.[1], [2], [5]
Confused deputy
Komponen berhak istimewa dibujuk memakai kewenangannya sendiri untuk pihak yang tak tepercaya. Di sini, agen penjelajah dan layanan Studio lokal menghubungkan konten halaman dengan pembuatan proses pada host.[1]

Pemetaan kerangka kerja

Kronologi Insiden

Sebelum 18-06-2026

Microsoft melaporkan perilaku ini ke MSRC; maintainer AutoGen memperkuat branch upstream dalam commit b047730. Laporan menyatakan rute MCP WebSocket terdampak belum disertakan dalam paket PyPI.[1], [2]

18-06-2026

Microsoft menerbitkan riset AutoJack dan panduan mitigasinya. Laporan menjelaskan demonstrasi lokal yang terkendali, bukan serangan terkonfirmasi terhadap pelanggan.[1]

Alur Serangan Langkah demi Langkah

Langkah 1

Agen diminta menjelajahi konten tak tepercaya

Prompt, halaman yang diambil, atau input tak tepercaya lain membuat agen penjelajah AutoGen menampilkan halaman yang dikendalikan penyerang pada workstation yang sama dengan AutoGen Studio.[1]

Langkah 2

Halaman yang ditampilkan mencapai WebSocket MCP lokal

Dalam konfigurasi yang diuji Microsoft, skrip halaman dapat terhubung ke endpoint MCP Studio di localhost. Daftar Origin tidak memisahkan control plane dari kode yang ditampilkan browser agen lokal.[1], [3]

Langkah 3

Rute MCP melewati middleware autentikasi normal

Middleware terdampak melewati jalur /api/mcp dengan asumsi handler WebSocket akan mengautentikasinya. Menurut Microsoft, handler tersebut tidak melakukan pemeriksaan itu.[1], [4]

Langkah 4

Query WebSocket menentukan parameter server

Rute pengembangan mendekode server_params dari URL menjadi StdioServerParams. Transport stdio MCP kemudian menggunakan command dan args untuk memulai proses anak.[1], [2], [5]

Langkah 5

Rantai mencapai akun host

Proses Studio lokal memulai proses anak yang dikonfigurasi dengan hak sistem operasinya sendiri. Microsoft menunjukkannya dalam PoC lokal yang terkendali dan menyatakan rute tersebut tidak ada di paket PyPI yang dirilis.[1], [2]

Kode Sumber: Rentan vs Aman

Contoh rentan mengurai pengaturan peluncuran yang dikendalikan pemanggil, tetapi sengaja tidak memulai proses. Perbaikan upstream memindahkan pengaturan dari query WebSocket ke status sesi sekali pakai di server dan mengaktifkan kembali autentikasi rute MCP. Pola defensif ini menambahkan kedaluwarsa, batas ukuran, penguncian konkurensi, dan daftar izin executable; ganti nama contoh dengan biner MCP yang secara eksplisit disetujui. Pola ini tidak menggantikan validasi Origin atau isolasi OS.[1], [2]

IMPLEMENTASI RENTAN
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:
    # Rute pengembangan memercayai konfigurasi yang diberikan melalui URL.
    encoded = query["server_params"]
    decoded = base64.b64decode(encoded)
    return StdioServerParams(**json.loads(decoded))
PERBAIKAN AMAN & KUAT
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 harus berasal dari middleware terverifikasi, bukan dari data permintaan.
    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]

Daftar Periksa Penguatan Sistem Rekayasa

Sumber