Uniswap和lendf.Me遭攻擊始末:願DeFi世界裡沒有ERC777

買賣虛擬貨幣

04月18日上午08:58開始,一 DeFi 平臺 Uniswap 被駭客利用重入漏洞實施了攻擊。大約24小時後,於04月19日(昨天)上午08:45,又一知名 DeFi 平臺 Lendf.Me 被以類似的手段實施了攻擊。

攻擊的原理是:攻擊者透過以太坊上少有 token 使用的 ERC777 的 transferFrom() 回撥機制,利用在內部呼叫_callTokensToSend()回撥函式劫持交易 ,並在真正更新餘額的 _move() 函式之前進行惡意攻擊。

在 Uniswap 的攻擊案例中,攻擊者利用此漏洞消耗盡 Uniswap ETH-imBTC 池約1,278個 ETH。而在 Lendf.Me 中,攻擊者則是利用它來任意增加內部 imBTC 抵押金額,並透過從其他可用的 Lendf.Me 交易中借入10多種資產(總價值約 2,524萬美元)。

詳細漏洞攻擊細節,我們將在文章後面做詳細介紹。

Figure 1: ERC777 transferFrom()

罪惡之源:ERC777 標準

我們首先介紹下 ERC777 標準,該標準是通用 ERC20 標準改進後的一個稀有標準,不僅實現了部分功能擴充套件還保持著 ERC20良好的相容性,願景是成為 ERC20標準的有效繼承者。

該標準擴充套件功能提供了“hook”機制, “透過呼叫“hook”函式來加強智慧合約對 Token 轉賬的通知或控制”。但也正因為此功能的出現給注入程式碼攻擊提供了可能性。

其中最關鍵的部分是,可以透過註冊 from 的 tokensToSend()來實行回撥。我們從下面的程式碼片段可以看到,ERC777 標準中可以透過 getInterfaceImplementer()(1,054行)獲得攻擊者的 tokensToSend() 介面,並在第1,056行呼叫此函式。而此處正是導致被劫持攻擊的原因。

Figure 2: ERC777-Compatible tokensToSend() Hijacking

如2019年4月OpenZeppelin 釋出的帖子以及2019年7月釋出的漏洞利用演示中所述,攻擊者可以自己定義函式 tokensToSend(),並透過 setInterfaceImplementer() 來設定合約中的 hook 函式。

Figure 3: OpenZeppelin's Exploit Demo (Hook Setup)

之後攻擊者就可以像傳統 PC 上的 hook 函式一樣,在 tokensToSend()做任何事情。如下圖所示,攻擊者可以對同一筆交易進行多次交易。

Figure 4: OpenZeppelin's Exploit Demo (Hook Function)

Uniswap攻擊分析

由於針對 ERC777 的攻擊原理在前面已經描述過了,因此我們不再贅述。

就如此惡意交易在 Bloxy中的截圖所示(hash:0x9cb1d93d6859883361e8c2f9941f13d6156a1e8daa0ebe801b5d0b5a612723c1),函式的內部呼叫中又進行一次 tokenToEthSwapInput()呼叫。這意味著攻擊者可以先透過交易操縱匯率,然後再用另一筆 imBTC 以較低價格兌換更多的 ETH。

Figure 5: Uniswap Hack

Lendf.Me攻擊分析

在 Uniswap 遭攻擊約24小時後,又一 DeFi 平臺 Lendf.Me 也遭到了駭客攻擊。下面是其中一個攻擊交易的截圖。如圖所示,supply() 函式中呼叫真實轉賬函式 transferFrom()時,被 hook 的攻擊者合約裡嵌入了盜用 Lendf.Me 的 withdraw() 的提幣操作。

Figure 6: Lendf.Me Hack

在這個交易例子中,攻擊者第一次 supply() 時確實向 Lendf.Me 存放了289.99999999個 imBTC,而在第二個 supply() 中,攻擊者只存放0.00000001個 imBTC,但由於攻擊者註冊了 tokensToSend(),所以在執行 doTransferIn() -> IMBTC :: transferFrom()(第1,583行)時,呼叫了攻擊者函式 tokensToSend(),攻擊者函式透過呼叫 Lendf.Me 的 withdraw() 函式把290個 imBTC 直接全部提走。

需要注意的是,正常的業務邏輯應該是專案合約中的 Balance 會減去被攻擊者提走的290個 imBTC,然而當 supply()執行返回時,餘額並未被重置,仍然為290 imBTC(第1,599行)。攻擊者就是透過控制修改 Lendf.Me 中攻擊者的 imBTC 抵押金額,有了足夠大的 imBTC 抵押,攻擊就可以從各種流動交易對中借出所有可用的10多種資產(資產總值25,236,849.44美元)。

Figure 7: Lendf.Me Hack Details

資產流向

攻擊者 0x538359 共計從 Lendf.Me 獲利 25,236,849.44 美元,其中各個 Token 分佈如下:

如上圖,攻擊者在獲利之後,馬上將各個 Token 轉移至其關聯賬號 0xa9bf70 之中,之後攻擊者數十次 透過 1inch.exchange, ParaSwap 等平臺將其中比較搶手的 WETH, PAX, BUSD 等 Token 換成 ETH, DAI, BAT 代幣,另外將其中的 TUSD, USDT 代幣存入 Aave 借貸平臺。至此為止,攻擊者及其關聯賬號的餘額如上所示。

修復建議

PeckShield 安全團隊在此建議開發者,可以採用 “Checks-Effects-Interactions”方法來防止這類重入攻擊。舉個例子,Lendf.Me 的supply() 裡如果是先更新 token 餘額,再呼叫 doTransferIn() 。這將會讓攻擊在 withdraw() 之後沒有重置餘額的可能性。

另一方面, ERC777 標準特性會不可避免地啟用 hook 機制,因此我們需要檢測並防止所有交易功能產生可以重入的風險。例如,如果 supply()和 withdraw()同時執行時加個互斥鎖,那麼攻擊者就無法在 supply()函式內部執行 withdraw()操作。

最後並不能被忽視的一點是,我們需要認真思考下是否ERC20已經足夠滿足專案需求,我們真的需要ERC777麼?

PS:此次駭客對 Lendf.Me 的攻擊對 DeFi 社羣來說無疑是一場災難,在此建議廣大 DeFi 開發者務必注意業務存在的系統性風控風險,應儘可能和第三方安全公司合作排查一切潛在的安全風險。更多詳情請點選左下角“閱讀原文”檢視英文原版分析報告。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;