创宇区块链安全实验室 水手 2021-04-20 14:50:41 发布在 区块链社区
8004 1
前言
前不久,DeFi 项目 Harvest.finance 遭受黑客攻击,黑客利用闪电贷套利 2400 万美元,涉及金额巨大,轰动一时
知道创宇区块链安全实验室 旨在通过全盘梳理攻击流程和代码细节,一窥闪电贷套利的秘密。
全盘梳理
基础信息
攻击者地址:0xF224ab004461540778a914ea397c589b677E27bb
攻击合约地址:0xc6028a9Fa486F52efd2B95B949AC630d287CE0aF
首次攻击 tx:0x35f8d2f572fceaac9288e5d462117850ef2694786992a8c3f6d02612277b0877
VaultProxy(fUSDC):0xf0358e8c3CD5Fa238a29301d0bEa3D63A17bEdBE
CRVStrategyStableMainnet:0xD55aDA00494D96CE1029C201425249F9dFD216cc
VaultYCRV:0xF2B223Eb3d2B382Ead8D85f3c1b7eF87c1D35f3A
CRVStrategyYCRVMainnet:0x2427DA81376A0C0a0c654089a951887242D67C92
convertor:0xfCA4416d9dEF20aC5b6Da8b8b322b6559770eFbF
*为方便起见,后面提到的地址均只用地址前 4 位代表
交易始末
从 tx0x35f8 中的代币转移记录中可以大致看出事件经过
详细的合约调用过程可通过以太坊交易分析平台载入交易 hash 进行分析
流程分析大致如上,事件概括起来即是攻击者 0xf224 部署了攻击合约 0xc602,然后一系列闪电贷攻击均在攻击合约的 0xfdb57542 方法中进行,其中核心流程就是通过 Uniswap 的 Flash Swap 进行闪电贷,先获得大量 USDT 和 USDC 为后续攻击做准备,然后重复执行如下动作:
- Curve ySwap 中进行 USDT=>USDC 的巨额兑换(巨额兑换造成 y 池中 USDC 价格上涨)
- USDC 质押存入 VaultProxy fUSDC 池( USDC 价格上涨,铸造出较平常更多的 fUSDC )
- Curve ySwap 进行 USDC=>USDT 回兑(1 步骤的逆操作,USDC 价格恢复)
- VaultProxy fUSDC 池中赎回 USDC ( USDC 价格回落,赎回出较平常更多的 USDC )
最后归还闪电贷并将获利的 USDC 兑换为 ETH 提取
代码细节
攻击合约未开源,暂时不作分析。可先从关键的 VaultProxy fUSDC 池合约 0xf035 的 deposit 函数入手,分析 fUSDC 的铸造量是如何计算的
从质押函数中可以看出 fUSDC 的铸造量是根据 fUSDC 总量和 USDC 策略的总投资量的比例来决定的
underlyingBalanceWithInvestment 函数实现如下:
fUSDC 池代理合约 0xf035 会进一步调用 CRVStrategyStableMainnet 策略合约 0xD55a 去进一步查询已投资的底层资产 USDT 的量
来到稳定币策略合约 0xD55a,investedUnderlyingBalance 函数实现如下:
这里的调用就稍微复杂一点了,从 ycrvVault 合约 0xF2B2 获取 shares 与 price,将乘积传入 underlyingValueFromYCrv 函数,结果与该合约 USDT 的量的和作为最后的函数返回值
我们先来看 ycrvVault 合约 0xF2B2
该金库合约 0xf2b2 本身继承了 ERC20,具有代币属性,从构造函数中可以看出代币代表 fyToken
也就是说上面获取的 shares 即是策略合约拥有的 fyToken 量
然后是 price,来看 getPricePerFullShare 函数:
可以明显看出 price 即是 yToken 对 fyToken 的占比,那么 shares 与 price 的乘积即代表策略合约所占有的 yToken 量,最后传入 underlyingValueFromYCrv 函数,在该函数中会调用 convertor.yCrvToUnderlying
这里就到了整个过程中最关键的地方了,也是问题的根本所在
convertor 合约 0xfCA4 并未开源
我们再次回到以太坊交易分析平台,查看整个 deposit 调用过程
可以看到前面的调用流程分析如实,并且 convetor 的调用最终会调用 Curve 的 Zap.calc_withdraw_one_coin,而该函数用于查询 lpToken 的赎回价
问题就在这里了,这里相当于就是向 Curve 问价,而调用传入的是 yToken 的量,那么返回的就是 yUSDC 兑换 USDC 的价格,即 USDC/yUSDC
而当前面巨额兑换 USDC 后,y 池中 USDC 价格上涨,那么相对价格 USDC/yUSDC 就会下跌。Harvest.finance 的 USDC 策略中 yUSDC 资产所具有的 USDC 净值经 calc_withdraw_one_coin 计算而来就损耗减少,最终反映到 deposit 函数的 fUSDC 铸造算法中,将导致 fUSDC 铸造量增加
总结
归根到底,Harvest.finance 被攻击的本质原因在于对策略稳定币价值的估价出现了问题,直接调用易被操纵价格的 Curve 的 calc_withdraw_one_coin 函数来估价,从而使攻击者有机可乘。这就是一次典型的喂价机制不完善导致的价格操纵的经济攻击事件。
知道创宇区块链安全实验室官网:www.knownseclab.com
知道创宇唯一指定存证平台:www.attest.im
联系我们:blockchain@knownsec.com
知道创宇区块链安全实验室导航
微信公众号
@ 创宇区块链安全实验室
微博
@ 知道创宇区块链实验室
https://weibo.com/BlockchainLab
知乎
@ 知道创宇区块链安全实验室
https://www.zhihu.com/org/zhi-dao-chuang-yu-qu-kuai-lian-an-quan-shi-yan-shi
Twitter
@KS_Blockchain_
https://twitter.com/KS_Blockchain