PeckShied:硬核技術解析,bZx協議遭駭客漏洞攻擊始末

買賣虛擬貨幣

02月15日,bZx 團隊在官方電報群上發出公告,稱有駭客對 bZx 協議進行了漏洞攻擊,且已暫停除了借貸外的其他功能。對於攻擊細節,bZx 官方並沒有進行詳細披露。

PeckShield 安全人員主動跟進 bZx 攻擊事件,發現這起事件是針對 DeFi 專案間共享可組合流動性的設計進行攻擊,特別在有槓桿交易及借貸功能的 DeFi 專案裡,該問題會更容易被利用。

Figure 1: Five Arbitrage Steps in bZx Hack

漏洞的攻擊細節如下:

此攻擊事件發生在北京時間 2020-02-15 09:38:57(塊高度#9484688)。攻擊者 的transaction 資訊可以在etherscan 上查到。此攻擊過程可以分為以下五個步驟:

第一步:閃貸獲取可用資金

攻擊者透過在部署的合約中呼叫了dYdX 閃貸功能借入10,000 ETH。這部分是已知的dYdX 的基本借貸功能,我們不做進一步解釋。

Figure 2: Flashloan Borrowing From dYdX

當第一步操作過後,如下表中攻擊者資產,此時並沒有收益:

第二步:囤積 WBTC 現貨

透過第一步閃貸獲得 ETH 後,攻擊者將其中的 5,500 ETH 存入 Compound 作為抵押品,貸出112 WBTC。這也是正常的 Compound 借貸操作,貸出的 WBTC 將在第四步中被拋售。

Figure 3: WBTC Hoarding From Compound

在此步驟操作後,我們可以看到關於攻擊者控制的資產發生了改變,但此時仍然沒有獲益:

第三步:槓桿拉盤 WBTC 價格

利用 bZx 的槓桿交易功能,做空 ETH 購入大量 WBTC。具體步驟是:攻擊者存入1,300 ETH 並呼叫 bZx 槓桿交易功能,即介面 mintWithEther(),在內部會繼續呼叫介面 marginTradeFromDeposit()。接下來,攻擊者將從 bZx 5倍槓桿獲得的5,637.62個ETH,透過KyberSwap 兌換成 51.345576 WBTC。請注意,此處做空 ETH 是借來的5倍。本次交易導致將 WETH / WBTC 的兌換率提高到109.8 ,大約是正常兌換率(~38.5 WETH / WBTC)的3倍。

為了完成此交易,KyberSwap 基本上會查詢其儲備金並找到最優惠的匯率,最終只有 Uniswap 能提供這樣的流通性,因此這個交易從本質上推動了 Uniswap 中 WBTC 價格上漲了3倍。

Figure 4: Margin Pumping With bZx (and Kyber + Uniswap)

應該注意的是,這步操作在合約內部實現有個安全檢查邏輯,但是實際上在交易之後並沒有驗證鎖倉值。也就是說,當攻擊發生時,此檢查沒有啟用,我們在後面會有一節詳細介紹此合約中的問題。

在這一步之後,我們注意到關於駭客控制的資產有以下改變。不過,在這一步之後仍然沒有獲利。

第四步:拋售 WBTC 現貨

在 Uniswap 中 WBTC 價格飆升後(價格為61.4 WETH / WBTC),攻擊者將第二步中透過 Compound 借的112 WBTC 全部賣給 Uniswap 並返還了相應的 WETH。這次交易攻擊者共計獲得6,871.41個 ETH 的淨額作為回報。在這一步之後,可以看到攻擊者已經獲得不少利潤。

Figure 5: WBTC Dumping With Uniswap

第五步:閃貸還款

攻擊者從拋售的 112 WBTC 中獲得的6,871.41 個ETH,將閃貸的 10,000個 ETH償還給 dYdX,從而完成閃貸還款。

在這一步之後,我們重新計算了以下資產詳情。結果顯示,攻擊者透過此次攻擊獲得71 ETH,加上這兩個鎖倉:Compound(+5,500weth/-112WBTC)和bZx(-4,337WETH/+51WBTC)。bZx 鎖倉處於違約狀態,Compound 的鎖倉是有利可圖的。顯然,在攻擊之後,攻擊者就開始償還 Compoud 債務(112BTC)以贖回抵押的5,500 個WETH。由於 bZx 鎖倉已經處於違約狀態,攻擊者也不再感興趣了。

參考 1WBTC=38.5WETH(1WETH=0.025BTC)的平均市場價格,若攻擊者以市場價格購入112 WBTC花費約需4,300個 ETH。此112 WBTC 用以清償 Compond 債務並取回抵押品5,500 ETH,則最終攻擊者總共獲利為 71 WETH +5,500 WETH -4,300 ETH=1,271 ETH,合計大約$355,880(當前ETH價格$280)。

硬核解析:bZx 可規避風險程式碼邏輯缺陷

透過前面攻擊者在合約中實現的步驟可以看出,問題的核心原因是在第三步呼叫 marginTradeFromDeposit() 透過借貸的1,300 ETH,加5倍槓桿來實現做空 ETH/WBTC 交易的,於是我們進一步審查合約程式碼,發現這是一個「可避免的套利機會」,但因為程式碼存在的邏輯錯誤造成可用於規避風險的程式碼邏輯沒有生效。具體程式碼追蹤如下:

首先是 marginTradeFromDeposit( ) 呼叫 _borrowTokenAndUse( ),此處由於是以存入的資產作槓桿交易,第四個引數為 true(第840行)。

在 _borrowTokenAndUse( ) 裡,當 amountIsADeposit 為 true 時,呼叫 _getBorrowAmountAndRate( ) 並且將 borrowAmount 存入 sentAmounts[1] (第1,348行)。

在 1,355 行,sentAmounts[6] 被設定為 sentAmounts[1] 並且於第1,370行呼叫 _borrowTokenAndUseFinal( )

經由 IBZx interface 進入 bZxContract 的 takeOrderFromiToken( ) 函式。

bZxContract屬於另一個合約 iTokens_loanOpeningFunctions於是我們我們繼續分析合約程式碼,在函式中發現有一個關鍵的邏輯判斷:

在第148行,bZx 事實上嘗試利用 oracle 合約的 shouldLiquidate( ) 檢查這個槓桿交易的倉位是否健康。然而,因為第一個條件(第146~147 行)已經為 true,則繼續執行,而忽略了 shouldLiquidate() 的邏輯判斷。

事實上,在合約 BZxOracle 的 shouldLiquidate( ) 中實現了對 getCurrentMarginAmount( ) <= loanOrder.maintenanceMarginAmount 判斷,如果執行到 shouldLiquidate( ) 就可以有效避免這個攻擊的發生。

如前所述,這是一次很有意思的攻擊,它結合了各種有趣的特性,如貸款、槓桿交易和拉高價格等。之所以可能發生這種攻擊,是因為當前專案共享可組合流動性的設計。特別是,5倍槓桿交易允許使用者以相對較低的成本借入大量代幣,加上 DeFi 專案間共享的流動性,導致交易價格更容易被操控。

免責聲明:

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

推荐阅读

;