Indexed Finance安全事件分析

学习Move安全开发实践,用这份综合指南创建Aptos应用。

Indexed Finance安全事件分析

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 绑定Token

DEFI5旨在为以太坊DeFi项目的前5大代币提供交易服务。具体来说,Indexed Finance会通过MarketCapSqrtController根据市值更新代币排名。 由于前5大代币的排序可能会随着时间推移而变化,DEFI5池子使用的代币数量可能会大于5个,如下方代码所示:

图1

图1显示,要绑定一个新代币,DEFI5必须触发由reindexTokens函数调用的_bind函数,而reindexTokens函数只能由MarketCapSqrtController的reindexPools函数调用,而reindexPools又只能由MarketCapSqrtController的reindexPool函数调用:

图2

图2演示了MarketCapSqrtController首先从市场获取代币信息(包括TotalSupply和价格),然后根据其市值计算排名。 在调用reindexPool函数时,会把排名前的代币地址作为参数传递给reindexTokens函数。 请注意,新添加的代币将被绑定到DEFI5,而不会替换DEFI5原有的代币。

0x2.2 接下来是什么?

代币绑定后,DEFI5必须将一个名为ready(表示代币状态)的变量设置为true才能启用交易:

图3

根据代码逻辑,除了合约初始化之外,ready变量只能在gulp函数中设置。 如图3所示,当DEFI5中的代币余额大于或等于_minimumBalances时,就会发生这种情况。 同时,初始代币权重(即denorm)将根据以下公式计算:

0x3. 漏洞分析

漏洞代码属于MarketCapSqrtController的updateMinimumBalance函数。

图4

如图4所示,updateMinimumBalance可以将一个ready为false的代币的minimumBalance修改为poolValue的1/100。poolValue的计算是漏洞的关键。

图5

图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函数,用借来的代币(以COMP为例)交换UNI。

注意1:这里多次调用是因为MAX_IN_RATIO的限制,一次最多只能交换一半的代币余额。

注意2:在此步骤中,poolValue将被严重低估,因为池子中UNI(作为firstToken)的余额大幅减少。

步骤4: 通过调用updateMinimumBalance函数来修改SUSHI的minimumBalance

请注意,由于步骤3中计算出的异常poolValueminimumBalance小于正常值。

步骤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提供流动性来交换LP代币。由于此时SUSHI的权重异常,可以获得更多的LP代币。

请注意,joinswapPoolAmountIn函数将根据收到的基础代币(在此例中为SUSHI)的权重来铸造LP代币。

步骤9: 通过调用exitPool函数,使用步骤8中获得的LP代币来耗尽池子。

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扩展。

迄今为止,公司已为MetaMask、Uniswap Foundation、Compound、Forta和PancakeSwap等300多家知名客户提供服务,并在两轮融资中从Matrix Partners、Vitalbridge Capital和Fenbushi Capital等知名投资者那里获得了数千万美元的投资。

官方网站: https://blocksec.com/

官方推特账号: https://twitter.com/BlockSecTeam

Sign up for the latest updates