合約遭受攻擊,損失數千萬美金,Avalanche 鏈上借貸項目 Vee Finance被黑分析

撰文:BlockSec

原標題:《似曾相識燕歸來:Vee Finance 安全事件分析》

北京時間 2021 年 9 月 21 日,Avalanche 鏈上的借貸項目 Vee Finance 的合約遭受攻擊,造成數千萬美金的損失(8804.7ETH 和 213.93 BTC)。

通過對相關合約和攻擊交易的分析,我們發現這是一起由於該項目提供的「槓桿借貸」功能不完善導致的價格操縱攻擊(即 price manipulation attack,我們的 論文 對此做了系統的闡述)。以下我們將對合約漏洞和攻擊流程進行完整的分析。

相關背景簡介

Vee Finance 是基於 Compound 的一個借貸項目,在 Compound 的基礎上做了一些優化和功能更新。而此次被攻擊的函數正是在這些新功能的一部分。

首先需要知道的是,Compound 是一個借貸平台,通過超額抵押進行借貸。Vee 在此基礎上提供了一個和攻擊有關的新功能:槓桿借貸。

超額借貸的意義在於,抵押價值 100 美元的 Ether 可以借出 80 美元的 USDC,當借貸者無法償還的時候可以沒收抵押物以償還債務,而只要市場波動在一定範圍內,抵押品的總價值一定大於貸出的價值,這樣就避免了現實世界中借貸需要依賴信譽這個問題。

但是這樣顯然不夠「刺激」。Vee 提供的槓桿借貸原理很簡單:使用者可以指定槓桿倍數,假如槓桿為 3,那麼抵押價值 100 美元的 Ether 可以借出 80*3=240 美元的 USDC。

當然,Vee 不會讓用戶直接拿走這筆錢(這樣貸出價值就大於抵押品價值,用戶可以直接違約),而是允許用戶做一筆交易。

這個邏輯在合約的 borrowAndCall 函數中實現:

合約遭受攻擊,損失數千萬美金,Avalanche 鏈上借貸項目 Vee Finance被黑分析

函數實現首先調用 borrowLeverageInternal 函數進行槓桿借貸,槓桿比率由參數 leverage 指定。然後調用 callOrderProxyInternal 執行用戶指定的交易(Order)。

下面首先來看 borrowLeverageInternal (內部會調用邏輯實現 borrowFresh)的實現。大部分代碼與 Compound 相同,只有處理 leverage 的時候存在不同:

合約遭受攻擊,損失數千萬美金,Avalanche 鏈上借貸項目 Vee Finance被黑分析

當用戶指定的槓桿為 0 時,執行正常的借貸邏輯,把資金直接轉給借貸人;當存在槓桿時,將槓桿記錄在狀態變量中,然後調用 doDeposit 函數將資金轉移到某個固定的位置(防止用戶直接取走)。

合約遭受攻擊,損失數千萬美金,Avalanche 鏈上借貸項目 Vee Finance被黑分析

需要注意的是,borrowLeverageInternal 的實現除了上述對 leverage 的處理之外,和原版 Compound 並沒有任何不同。也就是說,對於這一筆槓桿借貸,系統只會記錄單倍槓桿的借貸額。也就是說,雖然用戶用 100 美元抵押借出了 240 美元,但系統記錄的用戶借款仍然只有 80 美元。

doDeposit 函數的實現很簡單,將資金轉入 orderProxy 合約中。

合約遭受攻擊,損失數千萬美金,Avalanche 鏈上借貸項目 Vee Finance被黑分析

callOrderProxyInternal 函數的實現也很簡單,就是將用戶指定的 order 發給 orderProxy 進行執行。

總結一下:用戶可以進行抵押借貸。當使用抵押借貸時,資金不會轉給用戶,而是轉給 Vee 控制的 orderProxy 合約,後者會執行一個用戶指定的訂單。

漏洞成因分析

我們回顧一下一個正常的槓桿交易訂單是怎樣操作的:

  1. 假設我們擁有 1 個 Ether,想用槓桿來增加獲利,Vee 正好提供了槓桿交易的功能。
  2. 我們抵押了 1 個 Ether,根據超額抵押原則,能借出 0.5 個 Ether;再加上三倍槓桿,總共可以借出 1.5 個 Ether。
  3. 但此時項目方不能把這些 Ether 轉給我們,因為我們不能保證能夠還得上。因此 Vee 將這些借款鎖在一個代理(Proxy)合約中,由代理合約代為交易。
  4. 假設我們看好 LINK 代幣,想用槓桿放大獲利。假設此時市場價 1 LINK = 0.01 Ether。
  5. 為了(從我們對市場的觀點)中獲益,我們現在將 1.5 個 Ether 換為 150 個 LINK (第一筆交換),並讓 Vee 幫我創建一個限價單,當 1 LINK = 0.02 Ether 時將所有的 LINK 換回 Ether (第二筆交換)。這些功能正是 Vee 相對 Compound 的創新。
  6. 假設我們是正確的,一段時間后 LINK 的價格上漲。此時我們在 Vee 中的限價單執行,150 個 LINK 換到 3 個 Ether。訂單完成,我們獲利 1.5Ether。
  7. 假設我們是錯誤的,一段時間后 LINK 的價格下跌,Vee 會持續監控流動性情況,當可能產生資不抵債時,Vee 會主動將限價單取消並將所有的 LINK 賣出以清算我們的所有賬戶。

這個邏輯的正常執行基於以下條件:Vee 在槓桿交易開始時,必須立刻檢查第一筆交換的資金是否具有相同價值(在去除正常的滑點等因素之後),並持續監控用戶的流動性,在流動性不足時及時清算。在上述第 4 步中,1.5 個 Ether 和 150 個 LINK 的價值是基本等價的。否則,假設 150 個 LINK 的價值只有 0.5Ether,相當於 Vee 用 1.5 個 Ether 換到了價值只有 0.5 個 Ether 的代幣,會導致項目方嚴重虧損。

然而,我們在仔細分析了 Vee 項目的代碼之後,發現整個發起槓桿交易的 borrowAndCall 調用並沒有對第一筆交換的價值進行判斷。同樣以上面的例子為例,在調用 borrowAndCall 時,由於交易對不平衡等因素,1.5 個 Ether 只換回了 50 個 LINK (價值只有 0.5 個 Ether)。此時 Vee 項目方應該檢查這筆交換的價值,判定該調用失敗:因為兌換的兩種代幣價值不對等,此時用戶已經處於虧空狀態,項目方已經受損。遺憾的是,由於缺乏檢查,此次調用成功執行,此時項目方已經處於無法挽回的虧損狀態。

按道理來說,只要交易量足夠大,不平衡的交易對會由於套利等因素逐漸平衡。對於正常的 Pangolin 交易池來說也是如此,小額交易只會產生少量滑點,因此代理合約使用用戶的借出額進行交易,確實是將資金池內的某種代幣換成了價值相等的另一種代幣。

然而我們發現,作為 Vee 官方支持的 LINK 代幣,在其依賴的 Pangolin 交易池中竟然沒有交易對! Pangolin 項目的代碼和 Uniswap 基本一致,相同的代幣對只能創建一個資金池,而攻擊者利用的 ETH-LINK 資金池,正是由攻擊者自行創建的。當然,即使攻擊者沒有創建惡意的不平衡交易對,也可以用 Flash Loan 的方式使交易對不平衡。

綜上,可以推斷出下面的攻擊流程:

  1. 創造一個不平衡的交易對並在 Pangolin 上註冊。舉例來說,創造一個用 1 個 LINK 就可以換出 100 個 Ether 的交易對 P。
  2. 調用 borrowAndCall,加上槓桿,借出大量的 Ether。指定的訂單內容是在交易對 P 上將 Ether 換成 LINK。
  3. 訂單執行,交易對 P 中多了很多借出來的 Ether,還給 Vee 的代理合約的只是非常少量的 LINK。至此,攻擊者成功地「套」出了 Vee 的 Ether。
  4. 在交易對 P 上進行交易,用少量 USDT 換出前一步中套出的 Ether,獲得大量獲利。

總結來說,對於槓桿交易的實現,項目方應當在槓桿交易的開始就檢查第一筆交換的前後價值,如果嚴重不對等,此時用戶的流動性已經出現虧空,則應該直接使調用失敗,避免進一步的損失。

攻擊交易分析

以下,我們將以攻擊交易
0x4fb222908bd87cda0336776a6d78d35ef77b0a4bad866c4530b9f0d2616af005
為例介紹攻擊的主要流程。如圖所示(圖中省略了一些影響不大的合約調用)。

合約遭受攻擊,損失數千萬美金,Avalanche 鏈上借貸項目 Vee Finance被黑分析
  • 圖中步驟 1,攻擊合約一先為攻擊合約二往 VeeFinance 的 cToken 地址(合約名稱為 CErc20Immutable)存入了約 0.96WETH,從而使得合約二可以進行抵押借貸(可以借出約 0.52WETH)及槓桿交易。
  • 圖中步驟 2,合約一通過創建合約二的方式繞過了 isContract() 對 msg.sender 的檢查,並在 constructor() 函數中進行攻擊調用。
  • 圖中步驟 3~5,合約二調用 CErc20Immutable 的 borrowAndCall() 函數,正如前面代碼分析的,該函數在設置 leverage 倍數之後將進行槓桿交易,通過調用 VeeProxyController 去 Pangolin (類似於 UniswapV2)的池子中進行交易。注意,該池子是攻擊者在攻擊之前創建的交易對,所以池子的滑點受到攻擊者的控制,導致 VeeFinance 的合約用加了槓桿后的約 1.55 個 WETH 只換回了約 0.27 個 LINK,造成了大量虧損。
  • 圖中步驟 6~8,合約二還上借貸,取出圖中 1 的抵押物並 transfer 給合約一。前面代碼分析中指出,雖然攻擊者通過抵押槓桿進行了遠超抵押額的虧本交易,但是系統記錄的用戶借款卻仍然只有加了槓桿之前的借貸量。因此合約二僅需還上約 0.52 的 WETH,就能取出圖中 1 存入的 0.96WETH。
  • 圖中步驟 9,合約一以少量(約 0.27 個) LINK 換出了約 1.55 個 WETH,從而獲得了步驟 6~8 中 Vee 合約的虧損,從而攻擊獲利。

在我們分析的這筆交易中,攻擊者這樣的操作共進行了 5 次。而通過反覆利用漏洞連續攻擊,攻擊者的最終獲利頗為可觀。

總結

針對 DeFi 項目的攻擊已出現多次,不斷地給項目方和投資者帶來嚴重的損失。值得注意的是,本次攻擊的原理與 2020 年初發生的 bZx 安全事件第一次攻擊的原理非常相似。而相似的攻擊不斷重複表明,過往的經驗和教訓尚未得到應有的重視,DeFi 安全還有很長的路要走。

本文鏈接:https://www.8btc.com/article/6689130

轉載請註明文章出處

(0)
上一篇 2021-09-22 10:57
下一篇 2021-09-22 11:16

相关推荐