背景
Blast是由Blur的創辦人Pacman(Tieshun Roquerre、aka.鐵順)推出的Ethereum Layer2網絡,在2月29日啟動主網,目前約有19500 ETH和640000 stETH質押在Blast主網。
被攻擊的項目Munchables是Blast舉辦的Bigbang競賽獲勝的優質項目。
Blast官方對質押ETH在Blast主網的用戶會發放普通積分:
為了鼓勵用戶參與Blast生態上的DeFi項目,Blast官方會挑選出優質項目進行推薦,並鼓勵用戶將ETH二次質押到DeFi裡,可以獲得更快的積分增加速度以及黃金積分,因此有相當多的用戶將質押在Blast主網的ETH質押到了新創建的DeFi專案。
而這些DeFi計畫的成熟度、安全性仍有待考察,這些合約是否有足夠的安全考量來保管用戶的數千萬、甚至上億美元。
事件概述
Blast主網上線不到一個月,在2024年3月21日就發生了針對SSS Token(Super Sushi Samurai)的攻擊,Token合約中存在一個轉帳邏輯錯誤,導致攻擊者可以憑空增加指定帳戶的SSS Token餘額,最終項目損失了超過1310 ETH(約460萬美元)。
而在SSS Token攻擊事件過去不到一週的時間,Blast上又發生了一起更大的攻擊事件,Munchables專案被攻擊者一把捲走了17413.96 ETH,共約6250萬美元。
在這筆攻擊交易發生的半小時後,專案方合約裡的73.49 WETH也被駭客竊取的另一個地址。
此時專案方的合約地址上,還存著7276個WETH、7758267個USDB、4個ETH,隨時會落入駭客手裡,而駭客擁有拿走整個專案的所有資金的權限,共計約9700萬美元暴露於風險之中。
在事件後發生的第一時間,X(Twitter)的知名鏈上偵探zachXBT指出這次攻擊的根本原因是因為僱用了某國駭客所致。
讓我們深入看看,「某國駭客」是如何完成一次接近一億美元的攻擊。
現場還原
-
受害者發聲
[UTC+0]2024年3月26日21點37分(攻擊發生5分鐘後),Munchables官方在X(Twitter)發文表示遭到攻擊。
按鏈上偵探ZachXBT的調查,是因為他們有一位開發者是“某國黑客”,aavegotchi的創始人coderdannn也在X(Twitter)上表示:“Aavegotchi的開發團隊Pixelcraft Studios在2022時曾短期僱用過Munchables攻擊者來進行一些遊戲開發工作,他技術很糙,感覺確實像某國駭客,我們在一個月內解雇了他。他還試圖讓我們僱用他的一位朋友,那個人很可能也是一名駭客.”
由於這次攻擊讓社群的用戶損失巨大,我們立即啟動了鏈上調查,讓我們深入看看這個「某國駭客」的攻擊細節。
-
第一現場
[UTC+0]2024年3月26日21點32分,涉及17413.96 ETH的攻擊發生了。
透過Blastscan我們可以看到這筆攻擊交易:https://blastscan.io/tx/0x9a7e4d16ed15b0367b8ad677eaf1db6a2a54663610696d69e1b4aa1a08f55c95
受損合約(0x29..1F)是一個代理合約,存放了用戶質押的資金,我們可以看到,攻擊者調用了質押合約的unlock函數,並通過了所有的權限校驗,轉走了合約中所有的ETH到攻擊者位址1(0x6E..c5)。
看起來攻擊者呼叫了一個類似withdraw行為的unlock函數,取走了受損合約(0x29..1F)上大部分ETH。
是專案方的金庫忘了上鎖嗎?
受損合約(0x29..1F)中的unlock存在兩個相關校驗,我們一個一個來看。
首先,我們發現在校驗權限的流程中,調用了合約(0x16..A0)的isRegistered方法來查看當前的msg.sender,也即是黑客地址1(0x6E..c5)是否已經被註冊過:
答案是:通過了驗證。
這裡涉及到了合約(0x16..A0)以及其對應的最新的邏輯合約(0xe7..f1)
[UTC+0]2024年3月24日08點39分(攻擊發生的2天前),合約(0x16..A0)對應的邏輯合約被升級了。
邏輯合約升級交易:
https://blastscan.io/tx/0x9c431e09a80d2942319853ccfdaae24c5de23cedfcef0022815fdae42a7e2ab6
邏輯合約被更新至0xe7..f1。
最初的邏輯合約地址在這裡能看到,為0x9e..CD。
https://blastscan.io/tx/0x7ad050d84c89833aa1398fb8e5d179ddfae6d48e8ce964f6d5b71484cc06d003
此時,我們懷疑駭客是更新代理合約的邏輯實現合約,將為0x9e..CD變成惡意的0xe7..f1,完成了驗證權限的繞過。
真的是這樣嗎?
在Web3.0從來不需要猜測和聽信別人,你只需要掌握技術就能自己得到答案。
我們透過比較兩份合約(未開源合約),最初的0x9e..CD合約與更新後0xe7..f1存在一些明顯的區別:
0xe7..f1的initialize函數部分實作如下:
0x9e..CD的initialize函數部分實作如下:
可以看到,攻擊者在最初的邏輯合約(0x9e..CD)中,將攻擊者位址(0x6e..c5)設為register,同時還有其他兩個攻擊者位址0xc5..0d、0xbf.. 87也被register了,並且它們的field0被設定為初始化時的區塊時間,field0後面會解釋用處。
實際上,和我們猜測的恰好相反,真正的藏有後門的邏輯合約,反而是最初就存在,而後面更新的反而是正常的!
等等,這個更新出現在[UTC+0]2024年3月24日08點39分(攻擊發生的2天前),也就是在這個事件之前,邏輯合約已經變成沒有後門的合約了,為什麼後面攻擊者還可以完成攻擊?
這是因為delegatecall的原因,所以實際的狀態儲存更新是在合約(0x16..A0)中,這也就導致了即使之後邏輯合約被更新至沒有後門的邏輯合約0xe7..f1,合約(0x16. .A0)中被更改的slot依然不會恢復。
我們來驗證一下:
可以看到,合約(0x16…..A0)中對應的slot是有數值的。
這使得攻擊者能夠通過isRegistered方法的校驗:
攻擊者之後再將後門合約更換為正常合約掩人耳目,其實此時後門早已種下。
另外,在unlock的流程中,還牽涉到第二個校驗:
對於lock時間的檢查,這一部分是保證鎖定的資產不會在未到期就被轉走。
攻擊者需要保證當unlock被呼叫時的區塊時間大於要求的鎖定過期時間(field3)。
這一部分校驗就涉及受損合約(0x29..1F)以及對應的邏輯合約0xf5..cd。
在[UTC+0]2024年3月21日11點54分(攻擊發生的5天前)的交易中,
https://blastscan.io/tx/0x3d08f2fcfe51cf5758f4e9ba057c51543b0ff386ba53e0b4f267850871b88170
我們可以看到受損合約(0x29..1F)合約最初的邏輯合約是0x91..11,而在僅僅四分鐘後,就在
https://blastscan.io/tx/0xea1d9c0d8de4280b538b6fe6dbc3636602075184651dfeb837cb03f8a19ffc4f
被升級為了0xf5..cd。
我們同樣來比較兩份合約,可以發現攻擊者和之前一樣,也在initialize函數做了手腳,
0xf5..cd的initialize函數部分實作:
0x91..11的initialize函數部分實作:
可以看到,很明顯的,又是運用了同樣的手法,將自己持有的ETH數量&解鎖時間都進行了篡改,之後再替換回正常合約掩人耳目,當項目方和安全研究人員在Debug的時候,看到的邏輯合約全是正常的,而且由於合約均為未開源合約,更難以看清問題的核心。
至此,我們了解了這筆涉及17413 ETH的交易,攻擊者是如何做到的,但是這個事件背後的信息,只有這麼多嗎?
我們上面的分析中,其實看到駭客在合約內部內建了3個位址:
0x6e..c5(攻擊者位址1)
0xc5..0d(攻擊者位址2)
0xbf..87(攻擊者地址3)
而我們上面發現的攻擊交易只看到0x6e..c5,其他兩個位址都做了什麼?而且裡面的address(0)、_dodoApproveAddress、_uniswapV3Factorty到底還隱藏著什麼秘密?
-
第二現場
我們先來看看攻擊者地址3(0xbf..87),透過同樣的手法盜取了73.49 WETH:
https://blastscan.io/tx/0xfc7bfbc38662b659bf6af032bf20ef224de0ef20a4fd8418db87f78f9370f233
並且攻擊gas的來源位址(0x97..de),同時給0xc5..0d(攻擊者位址2)和0xbf..87(攻擊者位址3)都提供了手續費。
而攻擊gas來源位址(0x97..de)的0.1 ETH的資金源頭則來自owlto.finance(跨鏈橋)。
0xc5..0d(攻擊者地址2)收到手續費後,並未進行任何攻擊,但它其實肩負了一個隱藏的計劃,我們繼續看下去。
實際上,根據官方的事後救援交易,原來受損合約(0x29..1F)地址上並不止73.49枚weth,直到攻擊結束,也仍有7276.5 WETH & 7758267 USDB。
救援交易:
https://blastscan.io/tx/0x1969f10af9d0d8f80ee3e3c88d358a6f668a7bf4da6e598e5be7a3407dc6d5bb
原本攻擊者是打算竊取這些資產的,可以看到0xc5..0d(攻擊者位址2)這個位址原本是用來偷取USDB的。
這裡的_dodoApproveAddress為0x0000000000000000000000004300000000000000000000000000000000000003
為usdb的address
0xbf..87(攻擊者地址3)這個地址是用來偷取weth的:
這裡的_uniswapV3Factory為0x0000000000000000000000004300000000000000000000000000000000000004
為weth的address
而0x6e..c5(攻擊者地址1)負責盜用的是address(0),即是原生資產ETH。
攻擊者透過設定field0,即可透過以下邏輯對對應資產進行竊取:
問題
-
為什麼攻擊者沒有盜走所有資產?
理論上他可以盜取所有的資產,也就是剩下的WETH和USDB。
0xbf..87(攻擊者地址3)只盜取了73.49 WETH,0xbf..87(攻擊者地址3)其實完全可以把所有的7350 WETH拿走,也可以藉助0xc5..0d(攻擊者地址2 )把7758267 USDB全部拿走,為什麼只拿了一點點WETH就停下來了,我們不得而知,可能需要知名鏈上偵探深入內部的調查了。
https://blastscan.io/tx/0xfc7bfbc38662b659bf6af032bf20ef224de0ef20a4fd8418db87f78f9370f233
-
為什麼攻擊者沒有把17413ETH轉到Ethereum主網?
眾所周知,Blast主網是有可能透過中心化的方式攔截這些ETH,讓它永久停留在這裡,從而不會造成實質上的用戶損失,但是一旦這些ETH進入Ethereum主網,就沒有辦法攔截了。
我們評估了目前Blast的跨鏈橋,官方跨鏈橋沒有限制數量,但需要14天的退出時間,因此足以讓Blast官方來準備攔截的計劃。
而第三方的跨鏈橋是可以接近即時到帳的,就像攻擊者的手續費來源一樣,很快完成跨鏈,為什麼攻擊者沒有第一時間進行跨鏈?
實際上攻擊者在第一時間(攻擊的2分鐘內)進行了跨鏈:
https://blastscan.io/tx/0x10cf2f2b884549979a3a1dd912287256229458ef40d56df61738d6ea7d9d198f
而且資金花了20秒就在Ethereum主網到賬了,理論上攻擊者可以持續不斷進行跨鏈,可以在跨鏈橋人工幹預之前,將大量ETH跨鏈轉走。
至於為什麼只能每次3 ETH,原因是跨鏈橋的流動性限制,從Blast上跨到ETH上:
另一個支援Blast的跨鏈橋則支援的更少:
而在這一筆跨鏈交易後,攻擊者沒有繼續其他的跨鏈操作,原因我們不得而知,看上去“某國黑客”似乎是沒有為資金退出Blast進行充足的準備。
攻擊後的事件發展
根據社群使用者Nearisbuilding的回饋,他找到了更多攻擊者的身分訊息,並想辦法促使攻擊者歸還資金。
Found the Munchables exploiter telegram and discord and getting him to refund the $60 million
More details soon pic.twitter.com/xHJ4jkx5cM
— Near (@Nearisbuilding) March 27, 2024
最終,在加密社群的關注和努力下,「某國駭客」也許因為害怕暴露身份,向專案方提供了以上3個攻擊者地址的私鑰,並歸還了所有資金,專案方也進行了救援交易,將受損合約的資金全部轉到多簽合約進行保管。