全面揭露 bZx 閃電貸事件

全面揭露 bZx 閃電貸事件

全面揭露 bZx 閃電貸事件


​作者 | PeckShield


本文我們將介紹關於bZx攻擊者的交易行為,該攻擊者的交易行為最近佔據了推文與媒體的頭條。人們對該攻擊者的性質存在很多誤解。我們強調這不是針對Oracle的攻擊。相反,這是一個聰明的套利行為。該攻擊者利用了bZx智能合約執行中的一個漏洞,允許本應鎖定的bZx資金流向Uniswap,並進一步將流出資金吸收到一個Compound頭寸中。我將通過深入的利潤分析來揭露該攻擊者的行為。


全面揭露 bZx 閃電貸事件

bZx Hack I


全面揭露 bZx 閃電貸事件

Bzx事件的五個套利步驟


套利的五個步驟


罪魁禍首:

0xb5c8bd9430b6cc87a0e2fe110ece6bf527fa4f170a4bc8cd032f768fc5219838發生在2020年2月15日以太坊區塊高度9484688期間。如上圖所示,該攻擊可以分成5個不同的步驟:Flashloan Borrow, Hoard, Margin Pump, Dump, Flashloan Repay。接下來,本文將逐一拆分每個步驟。

第一步:Flashloan Borrow。該步利用閃電貸的特性從dYdX中借出10,000 個ETH。這一部分不做細講。

全面揭露 bZx 閃電貸事件


這步驟完成後,我們注意到攻擊者具有以下細分資產。但是還沒有產生收益。

全面揭露 bZx 閃電貸事件


第二步:Hoard。攻擊者將5, 500個ETH存入Compound作為抵押品,並借出112個WBTC。該步是正常Compound操作,而這些WBTC為第4步做準備。

全面揭露 bZx 閃電貸事件


完成此步驟後,我們看到攻擊者控制的資產發生了以下變化。但很顯然,還沒有任何收益。


全面揭露 bZx 閃電貸事件


第三步:Margin Pump。該攻擊者利用bZx的保證金交易功能做空ETH,這有利於WBTC(如sETHWBTCx5)。該攻擊者還存入1,300個ETH,發起bZx保證金交易。保證金交易利用KyberSwap將借入的5637.623762個ETH換成51.345576個WBTC。值得注意的是,做空ETH是借入的5倍。該互換實際上將1個WBTC的轉化率提高到109.8個WETH,大約是正常轉化率的三倍。

具體來說,為了完成這一交易,bZx將訂單轉發給KyberSwap, KyberSwap隨後會查詢其外匯儲備並找到最佳匯率。這一步驟實質上使Uniswap中的WBTC價格上漲了三倍。

全面揭露 bZx 閃電貸事件


需要注意的是,內置的完整性檢查應該阻止該步驟的行為,因為這個檢查會驗證頭寸在交換之後不會變成默認值。然而,當攻擊發生時,這個檢查並沒有生效,稍後我們將在之智能合約 bug部分檢查詳細信息。

在這一步之後,我們注意到攻擊者控制的資產發生了以下變化。然而,這一步之後仍然沒有產生收益。

全面揭露 bZx 閃電貸事件


第四步:Dump。隨著Uniswap中WBTC的價格飆升,攻擊者以Uniswap中的價格將借來的112 WBTC拋售。

全面揭露 bZx 閃電貸事件


該步驟將獲得6871.4127388702245個ETH的回報,總的轉化率為1WBTC=61.4WETH。

全面揭露 bZx 閃電貸事件


第五步:Flashloan Repay。利用112個WBTC轉換的6871.4127388702245個ETH,攻擊者將歸還10,000個ETH的dYdX閃電貸。

在此步驟後,我們重新計算資產明細。結果表明,攻擊者獲得了71個ETH的套利,再加上兩個頭寸,一個是Compound(+5,500WETH/-112WBTC),另一個是bZx(-4,337WETH/+51WBTC)。當bZx頭寸處於默認狀態時,Compound頭寸是非常有利可圖的。顯然,在攻擊之後,攻擊者馬上開始安排支付Compound債務,歸還112WBTC換回5,500weth抵押品。對於bZx頭寸,因為它已經處於默認狀態,所以攻擊者沒有表現出進一步的興趣。

全面揭露 bZx 閃電貸事件


考慮到1WBTC=38.5WETH(或1WETH=0.025BTC)的平均市場價格,攻擊者可以用4,300個ETH兌換112個WBTC。其結果是,加上之前的71.4個ETH,攻擊者大約獲利1,271個ETH,大約獲利355,880美元(當時ETH價格約為280美金)。

bZx智能合約中的bug


最神奇的是Uniswap中的WBTC/ETH價格是如何被操縱至高達61.4的利潤。如步驟3所述,WBTC/ETH的價格甚至在正常市場價格僅為38左右時,就被推高到了109.8。然而,如此巨大的價格下滑應該會導致bZx頭寸未完全抵押。但為什麼會允許抵押品不足,這自然會導致在bZx智能合約執行中發現隱藏的錯誤。

特別是,margin pump從marginTradeFromDeposit()這項功能開始。

全面揭露 bZx 閃電貸事件


如上如所示,marginTradeFromDeposit()利用840行設置為true的第四個參數調用_borrowTokenAndUse()。

全面揭露 bZx 閃電貸事件


在_borrowTokenAndUse()中,當amountIsADeposit為true時,在第1,348行調用_getBorrowAmountAndRate()。返回的borrowAmount將會存入sentAmounts[1]。

全面揭露 bZx 閃電貸事件


同樣地,在_borrowTokenAndUse()中,在amountIsADeposit為true時,第1,355行中的sentAmounts[1]的值會填充sentAmounts[6](稍後我們將看到這一點)。隨後,在第1,370行中調用_borrowTokenAndUseFinal()。

全面揭露 bZx 閃電貸事件


在第1414行,_borrowTokenAndUseFinal()通過IBZx接口調用takeOrderFromiToken(),以便交易流進入bZxContract。

全面揭露 bZx 閃電貸事件


有趣的部分來了。在第145至153行,由一個require()來檢查頭寸是否健康。不幸的是,在loadDataBytes.length == 0 && sentAmounts[6] == sentAmounts[1]例子中,完整性檢查bZxOracle::shoudLiquidate()將被跳過。這正是漏洞觸發避免完整性檢查的條件。

全面揭露 bZx 閃電貸事件


如果我們研究一下bZxOracle::shouldLiquidate(,第514行getCurrentMarginAmount() <= loanOrder.maintenanceMarginAmount可以通過捕捉margin pump步驟來完成檢查,從而防止這種攻擊。


全面揭露 bZx 閃電貸事件

bZx Hack II


2月18日,就在官方公佈黑客報告的幾小時後,bZx團隊再次暫停了該協議。

全面揭露 bZx 閃電貸事件

利用Oracle操作的五個步驟

PeckShield的研究人員使用內部實時異常檢測引擎檢測到異常的交易模式,並立即對其進行研究。第二次攻擊與第一次攻擊不同,它實際上是Oracle攻擊。在Oracle的操作中,它實際上採取了一種相反的方式,允許假定鎖定的bZx資金直接流向攻擊者控制的帳戶,而不需要進一步將資金歸還到Compound頭寸中。具體來說,Oracle操縱實質上提高了受影響數字貨幣(如sUSD)的價格,並使其在bZx貸款系統中極具價值。然後,攻擊者可以簡單地將之前購買或儲存的sUSD作為抵押品存入,而借入WETH以獲取利潤(而不是出售)。在上圖中,我們詳細展示了這一行為背後的五個步驟:Flashloan Borrow、Hoard、Pump、Collateralized Borrow、Flashloan payments。注意第四步是抵押借款,而不是拋售。接下來,我們將檢查每個特定的步驟。

獲利的五個步驟


該漏洞發生於2020年2月18日18:13:58時,以太坊區塊高度為9,504,627。罪魁禍首可以在etherscan上找到。如上述所言,這個攻擊過程可以分為以下5個步驟:

第一步:Flashloan Borrow。

這一步主要利用了bZx閃電貸的特點借入7,500個ETH。值得注意的是,這個步驟與第一次攻擊中的第一步相同。不同之處在於第二次攻擊使用bZx而不是dYdX來進行閃電貸。因此,我們不在此處過多討論該細節。

全面揭露 bZx 閃電貸事件


該步之後,我們注意到攻擊者具有以下資產細分。但是還沒有產生收益。

全面揭露 bZx 閃電貸事件


第二步:Pump。攻擊者通過Kyber分兩批將900個ETH交換為sUSD。第一批在KyberSwap中賣出540個ETH,在對儲備金進行內部諮詢之後,將掉期訂單傳遞至KyberUniswap儲備(0x31e085afd48a1d6e51cc193153d625e8f0514c7f)中,並得到92,419個sUSD回報。第二批價格是ETH的18倍,同樣在Kyber中,在對儲備金進行內部諮詢之後,將掉期訂單轉至Kyber-sUSD儲備(0x4cb01bd05e4652cbb9f312ae604f4549d2bf2c99),並得到63,584個sUSD。這兩批拋售有效地推動了sUSD的價格上漲到0.00899ETH(或1ETH=111sUSD)。與ETH/sUSD的平均市場價格相比,被操縱的價格大約高出平均市場價格的2.5倍。

通過以上操作後,攻擊者獲得92,419+63,584=156,003個sUSD。

全面揭露 bZx 閃電貸事件


此步驟後,我們注意到攻擊者控制的資產發生以下改變。但是仍沒有產生收益。

全面揭露 bZx 閃電貸事件


第三步:Pump。接下來,攻擊者轉向Synthetic Depot合同以市場價格獲得更多的sUSD。值得注意的是Synthetic Depot合同允許以公平價格的sUSD存入ETH。我們的分析顯示,攻擊者發送了6,000個ETH並回購了943,837個sUSD(由於沒有足夠的sUSD,有2,482個ETH被退回)。請注意,此步驟通常在Pump步驟之前啟動。無論出於何種原因,在這次的黑客攻擊中,情況並非如此(訂購不會影響最終結果,因為Synthetic Depot價格不受KyberSwap的影響)。

全面揭露 bZx 閃電貸事件


完成此步驟後,攻擊者將擁有100萬個sUSD,約佔sUSD總供應量的20%!我們還注意到攻擊者的資產發生了以下變化,但仍未產生任何收益。

全面揭露 bZx 閃電貸事件


第四步:Collateralized Borrow。正如之前所討論的那樣,到目前為止,攻擊者已經顯著地推高了sUSD/ETH價格,並擁有超過100萬個sUSD可供使用。請注意,在第一次攻擊中,攻擊者採取的方法是在一個有利可圖的Compound頭寸中利用上漲的價格。考慮到sUSD的流動性可能較低,攻擊者這次採取的方法是先將收集到的100萬個 sUSD抵押回bZx,然後向bZx借款6796個 ETH。由於bZx依靠Kyber作為價格來源,在sUSD/ETH價格飆升的情況下,擁有的1百萬個 sUSD可以借入6,796 個ETH。按照正常的1ETH=111 sUSD的兌換率,同樣數量的sUSD代幣只能購買4,000 個ETH,這說明這筆貸款已經資不抵債,擔保不足。

全面揭露 bZx 閃電貸事件

全面揭露 bZx 閃電貸事件


第五步:Flashloan Repay。借入的6,796個 ETH(加上剩餘的7,500—900—6,000—2,482=3,082 ETH),攻擊者可以將7,500 個ETH歸還給bZx,獲利2,378 個ETH的利潤。

全面揭露 bZx 閃電貸事件


簡單回顧一下,在這次交易中,攻擊者獲得2378 個ETH的利潤,並留下bZx頭寸(+1,099,841 sUSD/-6,796 ETH)。顯然,攻擊者沒有進一步的興趣,並帶走了2,378個 ETH,約合

665,840美元(假設ETH的價格為280美元)。


全面揭露 bZx 閃電貸事件

PeckShield 作者

Zoe Zhou 翻譯


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


分享到:


相關文章: