硬核解析 Uniswap 與 Lendf.Me 攻擊全過程


硬核解析 Uniswap 與 Lendf.Me 攻擊全過程


作者 | PeckShield
摘 要:Uniswap/Lendf.Me 攻擊事件發生的根本原因在於,ERC777標準中原生地支持hook(鉤子)機制的運行,而在imBTC實現過程中,並未在相應函數間加入互斥鎖,從而出現了重入攻擊漏洞。黑客正是利用該漏洞對Uniswap與Lendf.Me發起攻擊,以幾乎接近於0的成本獲得了鉅額的數字資產。
UTC時間2020年4月18日上午12:58:19發生了一起針對Uniswap imBTC流動池的重入漏洞攻擊。大約24小時後,UTC時間2020年4月19日上午12:58:43 Lendf.Me上也發生了類似的黑客攻擊事件。從技術上講,這兩起事件背後的主要邏輯是,兼容ERC777的“transferFrom()”的實現有一個回調機制,這可使得攻擊者可以在餘額真正更新之前(即在_move()內部),徹底地劫持交易並執行其他非法操作(通過“_callTokensToSend()”)。
具體來說,攻擊者利用該漏洞耗盡了Uniswap流動性池中的ETH-imBTC(約有1278個ETH),而Lendf.Me攻擊事件中,黑客利用該漏洞偽造了其在Lendf.Me中imBTC抵押品金額內部記錄,這樣他就可以從所有可用的Lendf.Me流動性池中借到10多種資產(資產總值為25,236,849.44美元)。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

根本原因分析
我們通過研究源代碼發現,“transferFrom()”操作的“from”地址註冊成為“implementer”(通過標準化ERC1820界面)時調用“tokensToSend()”函數的內在邏輯就是事件涉及的漏洞所在。為了清晰展示,我們在“tokensToSend()”函數被調用時對部分代碼進行截圖如下所示。就如在1054行顯示的那樣,ERC1820的“getInterfaceImplementer()”被用來收回已註冊的implementer。該函數有兩個參數:“from”和“TOKENS_SENDER_INTERFACE_HASH”,第一個參數其實是攻擊者參數(比如該參數可以向Lendf.ME提供imBTC),而第二個參數是一個常數,即“keccak256("ERC777TokensSender")”。在1056行的代碼中,“implementer”中定義的“tokensToSend()”函數被調用了,這使得攻擊者可以通過填入其他惡意執行代碼來黑進相應交易。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

正如OpenZeppelin在2019年4月的帖子和去年7月發佈的概念驗證(proof-of-concept)漏洞所描述的那樣,攻擊者可以通過“InterfaceImplementer()”與定義上文提到的“tokensToSend()”的智能合約設置鉤子(hook)。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

在“tokensToSend()”函數中,攻擊者可以填入其他的邏輯代碼,比如多次出售相同規模的Token。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

Uniswap攻擊事件
由於前面已經介紹了Uniswap攻擊事件背後的理論,所以我們在這篇博客中不再贅述。本文中我們研究了一個特定的惡意交易:(hash: 0x9cb1d93d6859883361e8c2f9941f13d6156a1e8daa0ebe801b5d0b5a612723c1)。顯然內嵌的“tokenToEthSwapInput()”又被調用了一次,這意味著攻擊者又進行了一次將imBTC兌換成ETH的交易(當兌換比率被攻擊者操縱至對其有利的水平時)。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

Lendf.Me攻擊事件


針對於Lendf.ME的黑客事件作用機制有所不同,但是本質上是一樣的。如果我們研究下以下惡意交易:(hash: 0xae7d664bdfcc54220df4f18d339005c6faf6e62c9ca79c56387bc0389274363b),由於在存入函數中,Lendf.Me中的“supply()”函數被嵌入一個額外的提現(withdraw())函數鉤子(hook),這導致攻擊者可以在不實際存入資產的情況下,偽造其imBTC抵押品餘額的內部記錄。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

攻擊者確實首先向Lendf.Me提供了確定數量的imBTC(即289.999999999 imBTC)。然而在第二個“supply()”中,攻擊者僅實際提供了0.000000001個imBTC,並且其後來通過“withdraw()”的鉤子又取出了290 imBTC(攻擊者黑入了“dotransferIn()”中“imBTC:transferFrom()”的調用——1583行)。從而使得290imBTC在鉤子函數“withdraw()”中從攻擊者的餘額中成功扣除。然而,當代碼又回執至“supply”時,餘額又被重置為290imBTC(1599行)。這就是攻擊者如何在lendf.Me中操縱imBTC抵押品餘額數量的內在邏輯。通過多次上述操作,攻擊者將其在Lendf.Me中的imBTC內部記錄刷到了足夠從多個流動池(總資產價值為25,236,849.44美元)中借出可用的10多種資產。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

緩解措施
作為阻止這種重入攻擊的緩解機制,Checks-Effects-Interactions確實能夠在這兩個事件中起到幫助。如果Lendf.Me“supply()”在存儲用戶的代幣餘額更新後調用“doTransferIn()”,攻擊者就不可能通過調用“withdraw()”重置餘額記錄。
從另一角度分析,ERC777原生地支持鉤子(hook)機制的運行,因此我們需要防止其所有交易類型的重入攻擊。比如,如果“supply()”和“withdraw()”都在函數起始部分設有互斥鎖,那麼攻擊者就不可能使用“supply()”中的“withdraw()”。最後但同樣重要的是,我們需要重新回顧一下在ERC20與 ERC777之間進行選擇時,選擇ERC777正是考慮到其能更好的保護用戶資產(因為每次交易操作時,不需要用戶將所有的Token都授權給平臺)。


寫在最後
Lendf.Me黑客事件對於當前的DeFi社區而言確實是一個打擊。在下面的圖片中我們收集了在該事件中各項資產的損失額數據。

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程


硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

PeckShield 作者

子銘 翻譯

Edward 編輯


內容僅供參考 不作為投資建議 風險自擔

版權所有 未經允許 嚴禁轉載


硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

硬核解析 Uniswap 與 Lendf.Me 攻擊全過程

☟☟☟


分享到:


相關文章: