在過去一週(2026/06/15 - 2026/06/21),我們觀察到 3 起值得關注的安全事件,總損失約為 $18.3M。
| 日期 | 事件 | 類型 | 估計損失 |
|---|---|---|---|
| 2026/06/18 | Aztec | 公開輸入綁定不當 | ~$2.2M |
| 2026/06/20 | LABUBU Token | 配置錯誤 | ~$1.1M |
| 2026/06/20 | jaredFromSubway | 授權管理不當 | ~$15M |
- Aztec:入選原因為該協議在三天內遭受第二次攻擊,此次透過其逃生艙電路(escape hatch circuit)被利用,凸顯了反覆出現的 ZK 證明綁定問題。
- jaredFromSubway:入選原因為該 MEV 機器人合約在未驗證消耗情況或撤銷剩餘授權的情況下,向不可信的代幣合約授予了批准(approval),使攻擊者得以累積並提取約 $15M。
Web3 最佳安全審計機構
在上線前驗證設計、程式碼與業務邏輯
本週精選:jaredFromSubway
與傳統的授權漏洞利用不同——傳統攻擊中,攻擊者利用受信任的 DeFi 合約中的漏洞,提取用戶已授權給這些合約的資產——此次攻擊的方向相反:MEV 機器人主動將自身資產的授權授予不可信的第三方合約,作為其套利操作的一部分。攻擊者構建了一個虛假的交易環境(本質上是一個蜜罐),虛假的交換池發出真實的 Swap 和 Sync 事件,而虛假代幣從未消耗已授予的授權額度,在提取之前持續累積這些向外的授權,據報總損失約為 $15M。
2026 年 6 月 20 日,以太坊上的 MEV 機器人操作者 jaredFromSubway 損失了約 $15M [1]。根據鏈上分析,根本原因是機器人合約中的授權管理不當:授權被授予給從未消耗它們的不可信包裝合約,攻擊者累積這些未消耗的授權額度,最終在單一交易中提取了機器人的真實餘額。
背景
jaredFromSubway 是以太坊上一位知名的 MEV 機器人操作者,專門從事三明治攻擊和鏈上套利。受害合約(0x1f2f...f387)是其操作錢包之一,持有大量 WETH、USDC 和 USDT 的運營資本。
這類 MEV 機器人必須動態地與鏈上出現的任意新代幣和資金池互動。它們監控記憶池(mempool)、模擬交易,並自動批准代幣互動以捕捉套利機會。這種操作模式依賴於一個假設:代幣的行為符合預期——當執行交換時,代幣合約會透過呼叫 transferFrom 來消耗已授予的授權額度。
漏洞分析
根本原因是 MEV 機器人在與不可信合約互動時,對授權的管理不當。
機器人在 Uniswap 資金池和路由器中執行各種套利路徑。在大多數互動中,機器人直接透過 transfer 將代幣推送至資金池,此時機器人本身是 msg.sender,不需要授權。然而,與包裝式代幣合約的互動採用拉取模型:機器人呼叫 wrapper.wrapTo(),在該呼叫內部,包裝合約會呼叫 realToken.transferFrom(bot, wrapper, amount) 來提取機器人的真實代幣。由於 transferFrom 期間的 msg.sender 是包裝合約而非機器人,因此需要事先進行 approve:
<real_token>.approve(tokenContract, amount)— 向包裝合約授予真實代幣的授權額度tokenContract.wrapTo()→ 跨資金池的多跳swap()→tokenContract.unwrap()— 包裝真實代幣,路由經過資金池,再解包回真實代幣
機器人假設 wrapTo() 會透過 transferFrom 消耗授權額度,就像一個行為正常的包裝合約那樣。然而,機器人從未驗證操作後授權額度是否實際被消耗,也未撤銷任何剩餘授權。如果 wrapTo() 不呼叫 transferFrom,完整的授權額度將在操作後保留,並成為持久性的攻擊面——任何持有此類授權的合約都可以在之後呼叫 transferFrom 來轉移機器人的真實資產。
攻擊分析
根據鏈上重建,攻擊者構建了一個包含三個組成部分的虛假交易環境,以利用上述漏洞:
-
虛假包裝代幣:每個虛假代幣使用真實代幣的名稱,但在符號前加上
f前綴(例如,名稱為USD Coin,符號為fUSDC,對應 USDC)。它實作了wrapTo()和unwrap()以模擬合法的包裝器,另外還有一個僅限攻擊者呼叫的withdraw()函數,透過transferFrom提取未消耗的授權額度。 -
虛假交換池:攻擊者透過自行部署的工廠合約部署了約 44 個 Uniswap V2 風格的資金池。這些資金池將虛假代幣相互配對,形成令人信服的交換路由。當呼叫
swap()時,這些資金池發出與合法交易無法區分的真實Sync和Swap事件。 -
攻擊者精心設計的利潤:在
unwrap()期間,虛假代幣透過transfer向機器人返還少量真實代幣。機器人收到了真實利潤,但這是由攻擊者刻意製造的,而非來自市場套利。
攻擊者透過外部合約中的逐區塊 getStatus() 開關來控制這些組成部分。當在與啟動交易相同的區塊中被呼叫時(啟動交易設定了 _getStatus = block.number),getStatus() 返回 1,否則返回 0。當 getStatus() == 0 時,wrapTo() 正常呼叫 transferFrom,授權額度被消耗。當 getStatus() == 1 時,wrapTo() 跳過 transferFrom——授權額度未被消耗——而 unwrap() 仍然向機器人返還攻擊者精心製造的代幣。攻擊者可能使用了 builder 賄賂,在希望累積授權時將啟動交易與機器人的交易放在同一區塊中。
攻擊分三個階段進行:
第一階段:部署攻擊基礎設施
-
步驟 1:攻擊者在區塊 25354424 至 25354519 之間跨區塊建立基礎設施。這包括部署虛假代幣工廠合約(0x81f2...0091)、透過自行部署的工廠創建約 44 個虛假 Uniswap V2 資金池、為資金池注入初始代幣餘額以確保
swap()呼叫能夠成功,以及向收割合約(0xb84d...df52)發送 0.01 ETH 用於 gas 費用和 builder 賄賂。 -
步驟 2:攻擊者透過 CREATE2 批量生產虛假包裝代幣,每個代幣模仿一個真實代幣(使用真實名稱但在符號前加
f前綴),並包含一個僅限攻擊者呼叫的withdraw()函數。CREATE2 提供了確定性地址,收割合約可以對其進行迭代。
第二階段:建立信任並累積授權
-
步驟 3(初始信任):在最早的交易中(例如,區塊 25354425 的 0x542d...362b),虛假代幣沒有
getStatus()開關——wrapTo()直接呼叫transferFrom,消耗了授權額度。機器人正常地進行授權、包裝、交換、解包並獲利。這確立了虛假代幣作為有利可圖的交易機會的形象。 -
步驟 4(持續信任):在後續交易中(例如 0x085e...37e51),
getStatus()開關已部署但返回 0(與啟動區塊不同)。wrapTo()仍然呼叫transferFrom並消耗授權額度。機器人持續獲利並保持互動。 -
步驟 5(累積):從區塊 25360519 的 0x8560...1915 開始,攻擊者透過 builder 賄賂將啟動交易與機器人的交易放在同一區塊中,使
getStatus()返回 1。在此模式下,wrapTo()跳過了transferFrom——授權額度未被消耗——但unwrap()仍然向機器人發送少量真實代幣。機器人看到了一次有利可圖的操作,並保留了授權。在約 600 個區塊(約 13 筆交易)中,機器人在WETH、USDC和USDT上重複此模式,在所有三種真實資產上累積了未消耗的授權額度。
第三階段:收割
- 步驟 6:攻擊者在收割交易 0x2be870...cf3e65 中對所有虛假代幣呼叫
withdraw(),利用未消耗的授權額度呼叫transferFrom,將機器人的真實餘額轉移給攻擊者。交易中包含 0.01 ETH 的 builder 賄賂以確保被打包進區塊。此次收割僅從受害合約中就提取了 1,474.58WETH+ 2,870,573USDC+ 2,035,760USDT(約 $7.5M)。
已識別的攻擊交易導致約 $7.5M 的損失,根據 jaredFromSubway 的聲明 [1],總損失約為 $15M。
結論
此次事件的根本原因是 MEV 機器人在與不可信合約互動時的授權管理不當。與傳統授權漏洞利用(攻擊者利用受信任的 DeFi 合約中的漏洞提取用戶已授權資產)不同,此次攻擊方向相反:機器人主動將自身資產授權給不可信的第三方合約,作為其套利操作的一部分。攻擊者累積了這些向外的未消耗授權,並在單一交易中將其全部提取。
為降低未來類似風險,與不可信代幣合約互動的機器人合約應在每次操作後驗證授權是否已被消耗,並撤銷任何剩餘授權。在一次看似成功的交易後仍存在未消耗的授權額度,是代幣惡意行為的強烈信號。
本週更多事件
Aztec
2026 年 6 月 18 日,Aztec 逃生艙 ZK 電路中缺少相等性約束,使攻擊者得以從以太坊上的舊版 RollupProcessor 合約中提取約 $2.2M(1,158 ETH、150K DAI 及約 0.47 renBTC)[2]、[3]。這是 Aztec 在三天內遭受的第二次攻擊(第一次攻擊已在我們的上一份報告中介紹,針對的是升級後的 RollupProcessorV3),兩次攻擊均源於相關的 ZK 電路公開輸入綁定漏洞。
背景
Aztec 的舊版 RollupProcessor 包含一個 escapeHatch 函數:這是一種安全機制,當 rollup 操作者停止處理時,允許任何人提交單一交易的證明。與 processRollup(需要授權提供者)不同,逃生艙根據區塊號以週期性視窗開放,任何人均可呼叫:
function escapeHatch(
bytes calldata proofData,
bytes calldata signatures,
bytes calldata viewingKeys
) external override whenNotPaused {
(bool isOpen, ) = getEscapeHatchStatus();
require(isOpen, 'Rollup Processor: ESCAPE_BLOCK_RANGE_INCORRECT');
processRollupProof(proofData, signatures, viewingKeys);
}
逃生艙使用專用的 ZK 電路(escape_hatch_circuit)來處理 join-split 交易:從默克爾樹中消耗輸入筆記並創建輸出筆記。電路必須驗證輸入筆記存在於當前數據樹中(使用 old_data_root 進行默克爾成員資格驗證),然後將相同的根作為公開輸入暴露,供 L1 合約與鏈上狀態進行驗證。
漏洞分析
漏洞位於逃生艙電路(escape_hatch_circuit.cpp)中。old_data_root 值被轉換為兩個獨立的見證變數,彼此之間沒有相等性約束。
第一個見證變數(第 33 行)被傳入 join-split 電路組件,用於默克爾成員資格證明,以驗證輸入筆記存在於數據樹中:
join_split_inputs inputs = {
// ...
witness_ct(&composer, tx.js_tx.old_data_root), // 第 33 行:第一個見證變數
// ...
};
auto outputs = join_split_circuit_component(composer, inputs);
第二個見證變數(第 50 行)被獨立創建並作為公開輸入暴露(第 88 行),Solidity 合約提取該值並與鏈上數據根進行比對:
auto old_data_root = field_ct(witness_ct(&composer, tx.js_tx.old_data_root)); // 第 50 行:第二個見證變數
// ...
composer.set_public_input(old_data_root.witness_index); // 第 88 行:作為公開輸入暴露
在 ZK 電路中,每次呼叫 witness_ct 都會創建一個獨立變數。在第 33 行和第 50 行之間沒有明確的 assert_equal 的情況下,證明者可以為這兩個見證變數賦予不同的值。在 Solidity 端,validateMerkleRoots 僅使用第 50 行的公開輸入檢查 require(oldDataRoot == dataRoot),無法查看第 33 行所使用的值。
同樣的未綁定模式也存在於
input_owner和output_owner:這些值在第 38–39 行被見證(傳入join_split_circuit_component進行所有權驗證),並在第 111–112 行再次被見證(作為獨立的公開見證暴露)。然而,我們尚未發現針對此漏洞的實際可利用路徑。
攻擊分析
逃生艙電路已從 aztec-connect 程式碼庫中移除 [4],但已部署的驗證器合約仍包含 EscapeHatchVk 驗證密鑰,因此使用有漏洞電路生成的證明仍然可以通過鏈上驗證。攻擊發生時,該合約已靜默 142 天,逃生艙視窗處於開放狀態。攻擊者的地址是透過 Union Chain 在攻擊發生前 14 小時才創建的 [2]。攻擊由三個核心步驟組成:
-
步驟 1:攻擊者構建了一棵包含任意面值的自擁有筆記的虛假默克爾樹。這些筆記並不存在於真實的鏈上數據樹(儲存所有有效筆記的默克爾樹)中。
-
步驟 2:攻擊者利用上述未綁定見證變數生成了一個逃生艙證明。第 33 行的見證變數(在 join-split 組件中用於默克爾成員資格驗證)被設為虛假默克爾根(成員資格檢查通過,因為偽造的筆記存在於虛假樹中,且所有權檢查也通過,因為攻擊者持有簽名密鑰)。第 50 行的見證變數(作為 Solidity 檢查的公開輸入暴露)被設為真實的鏈上數據根(Solidity 的
require(oldDataRoot == dataRoot)檢查通過,因為該值與合約儲存的根相匹配)。 -
步驟 3:在電路和 Solidity 檢查均通過的情況下,證明成功驗證。合約將逃生艙交易視為合法並釋放了資金。
攻擊者在三筆交易中重複此過程(0x9e1d6a...6b03ca、0xab306c...59c2b5、0x5c196c...4705c3),分別針對不同資產,共提取了 1,158 ETH、150K DAI 和約 0.47 renBTC,總計約 $2.2M。
結論
此次事件的根本原因是逃生艙電路中 old_data_root 的兩個見證變數之間缺少相等性約束。一個見證變數用於 join-split 組件內部的私有筆記成員資格驗證,另一個則作為 Solidity 檢查的公開輸入暴露。由於缺乏將兩者綁定的約束,攻擊者在對 L1 合約呈現有效鏈上根的同時,針對虛假默克爾樹證明了對偽造筆記的所有權。值得注意的是,從原始碼中移除有漏洞的電路並未使已部署的驗證器合約失效——舊版 RollupProcessor 上的 escapeHatch 函數在其區塊號視窗開放時仍然可以被呼叫。
為降低未來類似風險,當同一邏輯值出現在 ZK 電路的多個位置時,所有實例必須被明確約束為相等——對同一值進行獨立的 witness_ct 呼叫是一個綁定漏洞。電路審計應系統性地驗證每個公開輸入都與其所代表的電路內部值相綁定。
參考資料
- [1] jaredFromSubway 官方聲明(損失約 $15M)
- [2] Phalcon 警報:Aztec 逃生艙漏洞利用
- [3] Aztec Labs 官方聲明
- [4] 逃生艙電路移除,aztec-connect commit 8c3953a
關於 BlockSec
BlockSec 是一家全方位的區塊鏈安全與加密合規服務商。我們構建產品和服務,幫助客戶進行程式碼審計(包括智能合約、區塊鏈及錢包)、即時攔截攻擊、分析事件、追蹤非法資金,以及滿足 AML/CFT 合規義務,涵蓋協議和平台的完整生命週期。
BlockSec 已在頂級會議上發表多篇區塊鏈安全論文,揭露了多個 DeFi 應用程式的零日攻擊,攔截了多起駭客攻擊,救回超過 2000 萬美元,並保護了數十億美元的加密貨幣。
-
官方 Twitter 帳號:https://twitter.com/BlockSecTeam
-
🔗 BlockSec 審計服務 : 提交申請



