2023 年 8 月 3 日,Arbitrum 上的一個 MEV 機器人遭到攻擊,導致 80 萬美元的損失。此次攻擊的根本原因是用戶輸入驗證不足。
考慮到 MEV 機器人與其合約之間錯綜複雜且未經審計的交互,這表明不開源並不能保證安全性,對於 DeFi 協議尤其如此。
背景
MEV 機器人
MEV 機器人(最大可提取價值機器人)旨在識別並執行區塊鏈上的獲利機會。 它透過分析待處理交易(即記憶體池中的交易)或鏈上狀態來進行運作,以透過套利獲取利潤。
與典型的搶先交易和夾心攻擊 MEV 機器人不同,此次攻擊目標 MEV 機器人專注於執行三角套利和債務清算等策略。這些機器人本身有助於穩定自動做市商 (AMM) 的價格,並協助借貸協議進行清算以確保運作順暢,是 DeFi 生態系統健康運作的重要組成部分。
閃電貸 (Flashloan)
閃電貸是 DeFi 生態系統中的一項獨特創新——一種無抵押借貸形式。在閃電貸中,你可以在無需任何抵押品的情況下借入高達數十億美元的資金,前提是該貸款必須在同一筆交易內償還。如果貸款未能在該交易內償還,交易將會被撤銷,如同這筆交易從未發生過一樣。
此機制通常用於套利或利用其他 DeFi 策略來攫取暫時性的市場低效率獲利。
漏洞分析
簡述
由於對用戶輸入參數的驗證不足,攻擊者得以引入一個「虛假閃電貸提供商」(FakeFlashloanProvider)。金庫合約 (Vault contract) 使用該提供商發起閃電貸。隨後,或許是為了結算閃電貸,金庫合約授權代幣給該虛假提供商,從而導致資產被未經授權地轉移出金庫。
詳細說明
被攻擊的合約包括:
Vault(金庫):受害者合約 0xd614927acfb9744441180c2525faf4cedb70207f 作為一個「金庫」,提供儲備金並促進來自 AAVE 和 Balancer 等其他協議的閃電貸。Arbitrage Bot(套利機器人):易受攻擊的合約 0x8db0efee6a7622cd9f46a2cf1aedc8505341a1a7,作為「套利機器人」運作,在「金庫」合約中擁有借款人角色。
「套利機器人」中的 0x0582f20f() 函數是發起套利的主要入口點。
它首先調用「金庫」中的 borrow() 來獲取原始本金,然後透過 delegatecall 執行外部合約指定的套利邏輯,且此過程沒有任何驗證,該合約地址由 calldata 指定。
function 0x0582f20f(...) {
...
v67, /* uint256 */ v68 = address(0xd614927acfb9744441180c2525faf4cedb70207f).borrow(address(v39), address(v9[0]), v29).gas(msg.gas);
...
// 0x4da91757 = swap(address,address,address,uint256,uint256,uint256,address)
MEM[MEM[64] + 32] = uint224(address(MEM[0 + v4[v69]])) | 0x4da9175700000000000000000000000000000000000000000000000000000000;
v82 = address(v76 >> 96).delegatecall(MEM[(MEM[64]) len 228], MEM[(MEM[64]) len 0]).gas(msg.gas);
...
v189 = v170.refund(0x410085df, address(v9[0]), address(v39), v68, address(v9[0]), v29, v186, 4 + MEM[64] + (varg2.length << 5) - (4 + MEM[64]) + 192).gas(msg.gas);
...
}
隨後,它調用「金庫」中的 0x512b7351(),向攻擊者的 FakeFlashloanProvider 合約發起閃電貸。
0x512b7351() 函數要求 msg.sender 必須在白名單中,但由於之前的 delegatecall,該檢查被成功繞過。 這是非常關鍵的一步
function 0x512b7351(...) public nonPayable {
...
if (_borrow[msg.sender] >= 1) {
v0 = !_refund;
}
require(v0, Error('BBVault: FORBIDDEN'));
...
v38 = v23.length;
v39 = v23.data;
_refund = keccak256(v23);
...
<FakeFalshloanProvider>.flashloan(...);
...
}
在閃電貸回調期間,「金庫」中的 executeOperation() 首先將借入的資產轉移給「套利機器人」0x8db0ef,然後調用其 0x7fe3ba8b() 函數。
function executeOperation(...) {
...
require(_refund == keccak256(v3.data), Error('BBVault: STATUS'));
Token.transfer(ArbitrageBot, amountBorrowed);
<ArbitrageBot>.call(0x7fe3ba8b...);
}
「套利機器人」信任此外部調用,將接收到的資產轉回給 FakeFlashloanProvider。
然而,「金庫」並未識別出這一點,仍然在 executeOperation() 結束時向 FakeFlashloanProvider 授予代幣授權以償還閃電貸。
攻擊過程
攻擊交易:0x864c8cfb8c54d3439613e6bd0d81a5ea2c5d0ad25c9af11afd190e5ea4dcfc1f
攻擊者調用「套利機器人」的 0x0582f20f(),進而對攻擊者的合約執行了 delegate call。


hack_contract_2 隨後調用了「受害者」的函數 0x512b7351()。0x512b7351() 要求 msg.sender 在白名單中,但同樣因前述 delegatecall 而成功繞過檢查。

「受害者」隨後調用攻擊者的 FakeFlashloanProvider 合約,將所有閃電貸資產轉移給「受害者」,並觸發受害者的 executeOperation()。

「套利機器人」的 0x7fe3ba8b() 再次對攻擊者合約執行了 delegatecall,這次將所有資產轉回給了攻擊者。
此時,攻擊者「閃電貸提供商」借出的資產已經償還。

受害者(「金庫」)向 FakeFlashloanProvider 授予代幣授權,意圖償還閃電貸。

攻擊者利用這一授權獲利,使用 transferFrom 從受害者處掏空資金。
安全建議
非開源程式碼無法保證安全
認為非開源和混淆程式碼可以確保安全是一種誤解。這次 MEV 機器人事件揭示了保密性無法防禦攻擊,反而可能帶給開發者虛假的安全感。
嚴格的輸入驗證
對於合約互動和 calldata 進行細緻的驗證至關重要,特別是在涉及閃電貸和兌換回調等標準介面時。在合約設計和實現過程中,應優先確保資料的完整性和安全性。
閱讀本系列的其他文章:
- 導論:2023 年十大「精彩」安全事件
- #1:通過利用 Flashbots Relay 中的漏洞攻擊 MEV 機器人
- #2:Euler Finance 事件:2023 年最大的駭客攻擊
- #3:KyberSwap 事件:精妙利用舍入誤差與極其微妙的計算所進行的藝術級劫掠
- #4:Curve 事件:編譯器錯誤導致無害源代碼產生錯誤的字節碼
- #5:Platypus Finance:憑藉運氣倖免於三次攻擊
- #6:Hundred Finance 事件:引發易受攻擊的分叉協議中精度相關漏洞的浪潮
- #7:ParaSpace 事件:與時間賽跑,阻止業界最關鍵的攻擊
- #8:SushiSwap 事件:拙劣的救援嘗試引發了一系列模仿攻擊
- #10:ThirdWeb 事件:受信任模組之間的不相容暴露了漏洞



