​More than Re-entrancy : Revest Finance 被攻擊事件分析
BlockSec
2022-04-18 11:06
本文约5095字,阅读全文需要约20分钟
以太坊上的staking DeFi項目Revest Finance遭到黑客攻擊,損失約200萬美元。

二級標題

0. What's the Revest Finance FNFT

二級標題

二級標題

Revest Finance是針對DeFi領域中staking的解決方案,用戶通過Revest Finance參與的任何DeFi的staking,都可以直接生成一個NFT,即FNFT (Finance Non-Fungible Token) , 該NFT代表了這個staking倉位的當前以及未來價值。用戶可以通過Revest Finance 提供的3個接口和項目進行交互。質押自己的數字資產,mint 出相應的FNFT 。

•mintTimeLock : 用戶質押的數字資產在一段時間之後才能被解鎖。

•mintValueLock : 用戶質押的數字資產只有在升值或者貶值到預設數值才能被解鎖。

•mintAddressLock : 用戶質押的數字資產只能被預設的賬戶解鎖。

Revest Finance 通過以下3個智能合約完成對用戶存入的數字資產的鎖定和解鎖。

•FNFTHandler : 繼承自ERC-1155 token(openzepplin實現) 。每次執行lock操作時,fnftId會進行自增(fnftId 類似於ERC721中的tokenId)。 FNFT在被創建時,用戶需要指定它的totalSupply。當用戶想要提走FNFT背後的underlying asset,需要burn掉相應比例的FNTF。

•LockManage : 記錄FNFT被解鎖(unlock)的條件。

•TokenVault : 接收和發送用戶存入的underlying asset,並記錄每一種FNFT的metadata 。例如fnftId =1的FNFT背後質押的資產類型。

  • •unlocker : User X ->因為此次攻擊,黑客攻擊的入口是mintAddressLock函數,那麼我們以該函數為例,講述FNFT的生命週期。

  • User A調用Revest的mintAddressLock 函數

  • 只有User X 可以解鎖這筆資產•recipients : [User A , User B , User C] •quantities : [50 , 25 , 25] -> mint 數量為100 (sum (quantities)) , User A , User B , User C 各擁有50 , 25 ,25 枚。 •asset : WETH -> mint 出的FNFT 以WETH 為抵押品。 •depositAmount : 1e18 -> 每一枚FNFT 背後的抵押品數量為1枚WETH ( WETH decimal 為18 )

  • •fnftId : 1•unlocker : User X 

  • 假設當前系統中沒有其他FNFT, User A 通過mintAddressLock 與系統進行交互,FNFTHandler返回的fnftId = 1

  • •fnftId : 1•asset : WETH•depoistAmount : 1e18

LockManger 為其添加相應的記錄

Token Vault 為其添加相應的記錄

接著Token Valut 要從User A 這裡轉走100 * 1e18 數量的WETH 。

最後系統分別給User A , User B , User C mint 50 , 25 ,25 枚01-FNFT 。

通過mintAddressLock 函數鑄造FNFT 就完成了。

當User X 解鎖01-FNFT 後,用戶B 便可以通過withdrawFNFT提走underlying asset 。如圖二所示,User B 想要提取自己手中持有的25個01-FNFT 質押的數字資產。

協議首先檢查01-FNTF是否已經unlock ,如果已經unlock, 那麼協議會burn掉User B的25個01-FNFT,並給他轉25*1e18數量的WETH 。此時01-FNFT 的totalSupply 為75 。

Revest 合約還提供了另外一個接口,叫做depositAdditionalToFNFT,以便讓用戶為一個已經存在的FNFT 添加更多的underlying asset 。下面我們用2張圖描述它的“正常”用法。

這裡有三種情況

•quantity = 75 ->一.quantity == 01-FNFT.totalSupply() 如圖三所示

•amount = 0.5*1e18 ->以圖二中的場景為上下文,User A 要為01-FNFT 添加更多的抵押物。

為75個01-FNFT 追加質押。

每一枚01-FNFT 追加0.5*1e18 數量的WETH 。

於是User A 需要向Token Vault 轉入37.5*1e18 WETH (75 * 0.5*1e18) Token Vault 修改系統記賬,將depositAmount修改為1.5*1e18。現在每一枚01-FNFT 承載的資產為1.5*1e18 WETH 。

此時User C 調用withdrawFNFT ,burn掉他持有的25枚01-FNFT ,他可以拿走25*(1.5*1e18) = 37.5*1e18 WETH 。

於是,此時01-FNFT 的totalSupply為50 。

  • •quantity = 10 ->二.quantity < 01-FNFT.totalSupply() 如圖四所示

  • 以圖三中的場景為上下文,User A 繼續為01-FNFT添加更多的抵押物。< 01-FNFT.totalSupply()
    為10枚01-FNFT 追加質押。 •amount = 0.5*1e18 -> 為10枚01-FNFT 每一枚追加0.5*1e18 WETH

  • •fnftId : 2•asset : WETH•depositAmount : 2.0*1e18 (1.5*1e18 + 0.5*1e18) 

  • 由於quantity

  • 於是,User A 向協議支付5*1e18 WETH 系統將會burn掉10枚01-FNFT ,mint 出10枚02-FNFT ,並將burn掉的10枚01-FNFT承載的資產和User A 新轉入的資產,注入到02-FNFT中。於是就有

此時

二級標題

1. What't the Re-entrancy vulnerability

二級標題

二級標題
如圖五所示
第一步:

•depositAmount = 0 

•quantities = [2] 

第一步:

攻擊者調用mintAddressLock 函數

•depositAmount = 0

mint 出了2枚01-FNFT , 由於攻擊者將depositAmount 設置為0 ,因此他沒有轉入任何數字資產。相當於01-FNFT 背後承載的underlying asset 為0 。

第二步:攻擊者再次調用mintAddressLock 函數

•quantities = [360000] 準備mint 36w 枚02-FNFT depositAmount為0 。

•quantity = 1

•amount = 1*1e18

•fnftId = 1 

在mint 的最後一步,攻擊者利用ERC-1155 的call-back 機制重入了depositAdditionalToFNFT 函數。 (詳見下面給出的_doSafeTransferAcceptanceCheck函數)< fntfId.totalSupply(),在depositAdditionalToFNFT 中, 攻擊者傳入

最後攻擊者調用withdrawNFNFT函數,burn掉360,001枚02-FNFT,取走了360,001*1e18 RENA 。

  • 二級標題

2. the New Zero-day Vulnerability

二級標題

二級標題

在blockSecTeam團隊分析Revest Finance 的代碼時,handleMultipleDeposits 函數引起了我們的注意。

當用戶調用depositAdditionToNFT 函數追加抵押物時,該函數會改變FNFT 的depositAmount 。從代碼中我們可以發現,當newFNFTId != 0 時,該函數既改變了fnftId 對應的FNFT 的depositAmount 也改變了newFNFTId 對應的depositAmount 。
按照常理,當newFNFTId !=0 時,系統應該只記錄newNFTId 對應的depositAmount 。不應該改變fnftId 對應的depositAmount 。

我們認為這是一個非常嚴重的邏輯bug ,利用該漏洞,攻擊者可以很輕鬆提走系統中的數字資產。下面用3張圖描述模擬攻擊的原理。
假定the latest fnftId = 1

首先攻擊者調用mintAddressLock 函數,mint出360000個01-FNFT 。攻擊者將amount 設置為0 因此他不必轉入任何資產到Revest Finance 協議中。

•fnftId = 1

•amount = 1 * 1e18

•quantity = 1

mint 結束後,攻擊者擁有360000 枚depositAmount =0 的01-FNFT 。
然後攻擊者調用depositAdditionalToFNFT 函數,參數如下
協議轉走攻擊者amount * quantity 數量的代幣,即1 * 1e18RENA
協議會burn掉攻擊者1枚01-FNFT , 並為其鑄造一枚02-FNFT(假定latest fnftId = 2)

按照handleMultipleDeposits 函數中的邏輯, fnftId = 2 的資產,其depositAmount 會被設置為1.0*1e18。

正文

  • 正文

二級標題

二級標題

二級標題

二級標題

提升DeFi項目的安全性不是一件容易的事情。除了代碼審計,我們認為社區應該採取更加主動的方式,例如項目監控預警、甚至是攻擊阻斷使得DeFi社區更加安全。 (https://mp.weixin.qq.com/s/o41Da2PJtu7LEcam9eyCeQ).

參考文獻

*[1]: https://blocksecteam.medium.com/revest-finance-vulnerabilities-more-than-re-entrancy-1609957b742f

BlockSec
作者文库