0x.1 背景
2021 年 10 月 15 日 02:38(UTC+8),我們的內部監控系統捕獲到可疑的閃電貸交易:

經調查,我們發現這是一起針對 Indexed Finance 的價格操縱攻擊。具體而言,攻擊者利用該項目計算價格的公式缺陷發起了攻擊,並獲利 1600 萬美元。
社交媒體上已經對此進行了一些討論,該項目也發布了官方的事後分析報告(Indexed Attack Post-Mortem)。 然而,現有的分析並未對此次安全事件提供完整的理解。 因此,我們將在這篇部落格中提供全面的分析,包括項目的機制、漏洞、攻擊過程以及獲利情況。
0x1.1 相關合約地址
-
MarketCapSqrtController: 0x120c6956d292b800a835cb935c9dd326bdb4e011
-
DEFI5: 0xfa6de2697d59e88ed7fc4dfe5a33dac43565ea41
-
CC10: 0x17ac188e09a7890a1844e5e65471fe8b0ccfadf3
0x1.2 攻擊交易
-
攻擊交易 I: 0x44aad3b853866468161735496a5d9cc961ce5aa872924c5d78673076b1cd95aa
-
攻擊交易 II: 0xbde4521c5ac08d0033019993b0e7e1d29b1457e80e7743d318a3c27649ca4417
0x2. Indexed Finance 的機制
為了更好地了解該漏洞/攻擊,我們以 DEFI5(即被攻擊者駭入的資金池)為例來演示 Indexed Finance 的機制。
0x2.1 綁定代幣
DEFI5 旨在為以太坊上的 DeFi 項目中市值排名前 5 的代幣提供交易服務。具體而言,Indexed Finance 會通過 MarketCapSqrtController 根據市值更新代幣排名。 由於排名前 5 的代幣可能隨時間推移而變化,DEFI5 資金池中使用的代幣數量可能會大於 5,如下圖代碼所示:

圖 1 顯示,要綁定新代幣,DEFI5 必須觸發由 reindexTokens 函數調用的 _bind 函數,該函數只能由 MarketCapSqrtController 的 reindexPools 函數調用:

圖 2 演示了 MarketCapSqrtController 首先從市場獲取代幣信息(包括總供應量和價格),然後根據市值計算排名。
當調用 reindexPool 函數時,排名前列的代幣地址將作為參數傳遞以調用 reindexTokens 函數。
請注意,新加入的代幣將被綁定到 DEFI5,而不會替換 DEFI5 原有的代幣。
0x2.2 下一步是什麼?
在代幣綁定後,DEFI5 必須將一個名為 ready 的變量(表示代幣狀態)設置為 true 以啟用交易:

根據代碼邏輯,除了合約初始化之外,ready 只能在 gulp 函數中設置。
如圖 3 所示,當 DEFI5 中的代幣餘額大於或等於 _minimumBalances 時,才會執行此操作。
同時,初始代幣權重(即 denorm)將根據以下公式計算:

0x3. 漏洞分析
漏洞代碼屬於 MarketCapSqrtController 的 updateMinimumBalance 函數。

如圖 4 所示,updateMinimumBalance 可以將 ready 為 false 的代幣的 minimumBalance 修改為 poolValue 的 1/100。poolValue 的計算是該漏洞的關鍵。

圖 5 的計算僅僅實現了以下公式:

然而,該公式存在兩個潛在問題:
- 使用單個代幣的流動性來推算整個資金池的價值;
- 資金池的權重(
_totalWeight)和代幣的權重(token.denorm)不受流動性變化的影響。事實上,它們受外部市場市值的影響。此外,它們的變化受到時間週期的限制,即每小時增減 1%。
簡而言之,攻擊者可以利用閃電貸瞬間對某個代幣的流動性造成巨大影響,從而操縱 poolValue,而此時資金池和代幣的權重並未相應改變。
通過這種方式,攻擊者可以操縱代幣(ready 為 false)的 minimumBalance,進而發動價格操縱攻擊。
0x4. 攻擊分析
攻擊包含以下 9 個步驟:
第 1 步:調用 reindexPool 函數以綁定 SUSHI。在調用之前,DEFI5 資金池中有 6 種代幣,包括 UNI、AAVE、COMP、SNX、CRV 和 MKR。由於 SUSHI 的市值進入前 5 名,SUSHI 將被添加到資金池中。

第 2 步:通過 SushiSwap 借入 IndexPool 支持的所有 6 種代幣(UNI、AAVE、COMP、SNX、CRV 和 MKR)。

第 3 步:通過使用借入的代幣多次調用 swapExactAmountIn 函數來交易 UNI(以 CMOP 為例)。
注意 1:此處進行多次調用是因為
MAX_IN_RATIO的限制,每次最多只能交換代幣餘額的一半。注意 2:在此步驟中,
poolValue將被嚴重低估,因為資金池中 UNI(作為firstToken)的餘額大幅減少。

第 4 步:通過調用 updateMinimumBalance 函數修改 SUSHI 的 minimumBalance。
請注意,由於第 3 步中計算出的異常
poolValue,minimumBalance將小於正常數值。

第 5 步:通過調用 joinswapExternAmountIn 函數提供流動性來準備 LP 代幣。我們將看到這些 LP 代幣被用於換回更多的 SUSHI。
請注意:由於
MAX_IN_RATIO的影響,joinswapExternAmountIn函數需要被多次調用。

第 6 步:通過先借入大量 SUSHI 並將其轉移到資金池,然後調用 gulp 函數將 SUSHI 的 ready 設置為 true,從而操縱 DEFI5 資金池中 SUSHI 的權重。通過這種方式,SUSHI 的初始權重(denorm)變為一個很高的數值。

第 7 步:通過調用 exitPool 函數將 LP 代幣換回基礎代幣(UNI、AAVE、COMP、SNX、CRV、MKR 和 SUSHI)。
注意:
exitPool函數不考慮每個代幣的權重,因此,基礎代幣將以相等的比例退回。
第 8 步:通過調用 joinswapExternAmountIn 函數並使用 SUSHI 提供流動性。由於此時 SUSHI 權重異常,可以收穫更多的 LP 代幣。


請注意,joinswapPoolAmountIn 函數將根據接收到的基礎代幣(此處為 SUSHI)的權重鑄造 LP 代幣。
第 9 步:通過使用第 8 步中獲得的 LP 代幣調用 exitPool 函數,抽乾資金池。
0x5. 利潤分析
我們的調查顯示,攻擊者使用的所有資金(包括交易費用)都來自 Tornado Cash。
總共有兩筆攻擊交易:
-
在第一筆交易中,攻擊者獲利:6,226.8 AAVE, 15 ETH, 192,358.6 UNI, 5,459.5 COMP, 721,611.3 CRV, 16,680.6 SNX, 406.5 MKR。
-
在第二筆交易中,攻擊者獲利:109.6 MKR, 17,844 UMA, 1,002.4 COMP, 34,602.5 UNI, 131,645.4 BAT, 28,754.1 SNX, 1,273.6 AAVE, 124,194.2 CRV, 33,215.4 LINK, 5.24 YFI。
此外,攻擊者還進行了幾次失敗的嘗試。
截至撰寫本文時,攻擊者獲利的價值為 1600 萬美元,且尚未轉移。
關於 BlockSec
BlockSec 是一家開拓性的區塊鏈安全公司,由一群全球知名的安全專家於 2021 年創立。公司致力於增強新興 Web3 世界的安全性和可用性,以促進其大規模採用。為此,BlockSec 提供智能合約與 EVM 鏈的安全審計服務、用於安全開發和主動阻止威脅的 Phalcon 平台、用於資金追蹤與調查的 MetaSleuth 平台,以及幫助 Web3 構建者高效遨遊加密世界的 MetaSuites 擴展插件。
迄今為止,公司已服務超過 300 家知名客戶,如 MetaMask、Uniswap Foundation、Compound、Forta 和 PancakeSwap,並獲得了包括經緯創投 (Matrix Partners)、Vitalbridge Capital 和分佈式資本 (Fenbushi Capital) 在內的卓越投資者兩輪數千萬美元的融資。
官方 Twitter 賬號:https://twitter.com/BlockSecTeam



