據慢霧區情報,以太坊 DeFi 平台 Lendf.Me 遭受重入漏洞攻擊。慢霧安全團隊在收到情報後隨即對此次攻擊事件展開分析,並快速定位了問題所在。
據慢霧科技反洗錢(AML)系統初步統計分析,Lendf.Me 被攻擊累計的損失約 24,696,616 美元,具體盜取的幣種及數額為:
- WETH:55159.02134
- WBTC:9.01152
- CHAI:77930.93433
- HBTC:320.27714
- HUSD:432162.90569
- BUSD:480787.88767
- PAX:587014.60367
- TUSD:459794.38763
- USDC:698916.40348
- USDT:7180525.08156
- USDx:510868.16067
- imBTC:291.3471
之後攻擊者不斷通過 1inch.exchange、ParaSwap、Tokenlon 等 DEX 平台將盜取的幣兌換成 ETH 及其他代幣。
延伸閱讀:突發!dForce證實Lendf.me超過 99% (7.5 億資產)遭駭,楊民道:現在不要存款
延伸閱讀:dForce楊民道發布「東山再起」宣言,駭客正試圖與他們聯繫
以下是詳細分析過程。
攻擊細節
本次對 Lendf.Me 實施攻擊的攻擊者地址為0xa9bf70a420d364e923c74448d9d817d3f2a77822
,攻擊者通過部署合約0x538359785a8d5ab1a741a0ba94f26a800759d91d
對 Lendf.Me 進行攻擊。
通過在 Etherscan 上查看攻擊者的其中一筆交易:
我們發現,攻擊者首先是存入了 0.00021593 枚 imBTC,但是卻從 Lendf.Me 中成功提現了 0.00043188 枚imBTC,提現的數量幾乎是存入數量的翻倍。那麼攻擊者是如何從短短的一筆交易中拿到翻倍的餘額的呢?這需要我們深入分析交易中的每一個動作,看看究竟發生了什麼。
通過把該筆交易放到 bloxy.info 上查看,我們能知道完整的交易流程
透過分析交易流程,我們不難發現攻擊者對 Lendf.Me 進行了兩次 supply() 函數的調用,但是這兩次調用都是獨立的,並不是在前一筆 supply() 函數中再次調用 supply () 函數。
緊接著,在第二次 supply() 函數的調用過程中,攻擊者在他自己的合約中對 Lendf.Me 的 withdraw() 函數發起調用,最終提現。
在這裡,我們不難分析出,攻擊者的 withdraw() 調用是發生在 transferFrom 函數中,也就是在 Lendf.Me 通過 transferFrom 調用用戶的 tokensToSend() 鉤子函數的時候調用的。很明顯,攻擊者通過 supply() 函數重入了 Lendf.Me 合約,造成了重入攻擊,那麼具體的攻擊細節是怎樣的呢?我們接下來跟進 Lendf.Me 的合約代碼。
代碼分析
Lendf.Me 的 supply() 函數在進行了一系列的處理後,會調用一個 doTransferIn 函數,用於把用戶提供的幣存進合約,然後接下來會對 market 變量的一些訊息進行賦值。回顧剛才說的攻擊流程,攻擊者是在第二次 supply() 函數中通過重入的方式調用了 withdraw() 函數提現,也就是說在第二次的 supply() 函數中,1590 行後的操作在 withdraw() 之前並不會執行,在 withdraw() 執行完之後,1590 行後的代碼才會繼續執行。這裡的操作導致了攻擊者可提現餘額變多。
我們深入分析下 supply() 函數
根據上圖,可以看到,在 supply() 函數的末尾,會對 market 和用戶的餘額進行更新,在這之前,用戶的餘額會在函數的開頭預先獲取好並保存在 localResults.userSupplyCurrent,如下:
通過賦值給 localResults 變量的方式,用戶的轉入訊息會先暫時保存在這個變量內,然後此時攻擊者執行 withdraw() 函數,我們看下 withdraw() 函數的代碼:
這裡有兩個關鍵的地方:
1、在函數的開頭,合約首先獲取了 storage 的 market 及 supplyBalance 變量。
2、在 withdraw() 函數的末尾,存在同樣的邏輯對 market 用戶的餘額訊息(supplyBalance) 進行了更新,更新值為扣除用戶的提現金額後的餘額。
按正常的提現邏輯而言,在 withdraw() 單獨執行的時候,用戶的餘額會被扣除並正常更新,但是由於攻擊者將 withdraw() 嵌入在 supply() 中,在 withdraw() 函數更新了用戶餘額(supplyBalance))後,接下來在 supply() 函數要執行的代碼,也就是1590 行之後,用戶的餘額會再被更新一次,而用於更新的值會是先前 supply() 函數開頭的保存在 localResults 中的用戶原先的存款加上攻擊者第一次調用 supply() 函數存款的值。
在這樣的操作下,用戶的餘額雖然在提現後雖然已經扣除了,但是接下來的 supply() 函數的邏輯會再次將用戶未扣除提現金額時的值覆蓋回去,導致攻擊者雖然執行了提現操作,但是餘額不但沒有扣除,反而導致餘額增加了。通過這樣的方式,攻擊者能以指數級別的數量提現,直至把 Lendf.Me 提空。
防禦建議
針對本次攻擊事件慢霧安全團隊建議:
1. 在關鍵的業務操作方法中加入鎖機制,如:OpenZeppelin 的 ReentrancyGuard
2. 開發合約的時候採用先更改本合約的變量,再進行外部調用的編寫風格
3. 項目上線前請優秀的第三方安全團隊進行全面的安全審計,盡可能的發現潛在的安全問題
4. 多個合約進行對接的時候也需要對多方合約進行代碼安全和業務安全的把關,全面考慮各種業務場景相結合下的安全問題
5. 合約盡可能的設置暫停開關,在出現「黑天鵝」事件的時候能夠及時發現並止損
6. 安全是動態的,各個項目方也需要及時捕獲可能與自身項目相關的威脅情報,及時排查潛在的安全風險
附:OpenZeppelin ReentrancyGuard-https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol
?相關報導?
存款年利率42%引爆擠兌危機!台灣技術團隊連夜打造 DeFi 逃脫裝置「拯救世界」的故事
DeFi 新手介紹|合成資產發行平台 Synthetix 五分鐘概述
DeFi Dad 受紐約餐廳啟發推出「 DeFi 晚餐債券」,助小型企業度過營運危機
讓動區 Telegram 新聞頻道再次強大!!立即加入獲得第一手區塊鏈、加密貨幣新聞報導。
LINE 與 Messenger 不定期為大家服務