前言
北京时间 2022 年 3 月 22 日,知道创宇区块链安全实验室 监测到 Fantom 生态稳定币收益优化器 OneRing Finance 遭到闪电贷攻击,黑客窃取逾 145万 美元。
分析
攻击事件如下图所示,该次攻击事件的问题点在于 OneRing Finance 直接使用交易对中的 reserves 来实时进行 OShare 的价格计算,攻击者通过 Swap 操作提高 reserves 的量,最终拉升 OShare 的价格,获取更多的资金。
基础信息
攻击合约: 0x6A6d593ED7458B8213fa71F1adc4A9E5fD0B5A58(已自我销毁)
攻击者地址: 0x12EfeD3512EA7b76F79BcdE4a387216C7bcE905e
攻击 tx: 0xca8dd33850e29cf138c8382e17a19e77d7331b57c7a8451648788bbb26a70145
漏洞合约: 0xc06826f52f29b34c5d8b2c61abf844cebcf78abf
流程
攻击者的核心攻击流程如下:
1.攻击者从 USDC/MIM 交易对中闪电贷借出 8000W 的 USDC到攻击合约中。
2.攻击者使用 swap 把 1 USDC 兑换成 1.001 miMATIC。
3.使用 depositSafe 存入 79999997 的 USDC。
4.合约 mint 给攻击者 41965509 OShare,此时攻击者还有 2 USDC 和 1.001 miMATIC。
5.攻击者将 2 USDC 和 0.326 miMATIC 添加流动性,获得 7.82 x 10-7 spLP。
6.将 41965509 的 OShare withdraw 兑换成 81534750 的 USDC。
7.移除流动性获取 0.790 USDC 和 0.862 miMATIC。
8.把 1.501 miMATIC 兑换成 1.433 USDC。
9.归还 8000W USDC 和 80080 USDC 的手续费,最净获得 1534750 - 80080 = 1454670
USDC
细节
攻击者在攻击之前通过 Celer Network 的 cBridge 跨链获得了发起攻击所需的 gas。
通过流程第二步我们可以看出当时 USDC 和 miMATIC 的兑换率为约 1:1.001,而通过流程第三步和第四步的 swap
后可以在第五步中看到 USDC 和 miMATIC 的兑换率变成了约 1:0.163,所以我们需要重点分析 deposit
函数和 withdraw
中计算价格的问题。
分析交易的 Debugger,可以看到调用的是 depositSafe
函数。
查看 depositSafe
函数,其内部会调用 _deposit
函数,继续跟进,发现在 _deposit
函数中又调用了 _doHardWorkAll
函数,最后 mint
OShare 给攻击者。
而在 _doHardWorkAll
函数中使用了 for 循环将部分存入的 USDC 全部兑换成其他的代币。
执行此次 depositSafe
函数时 getSharePrice
计算的 OShare 价格为 1062758591235248117
。
接下来我们看下 getSharePrice
函数的代码,在 getSharePrice
函数中调用了 balanceWithInvested
函数,而在 balanceWithInvested
函数中又调用了 investedBalanceInUSD
函数。
继续跟进,investedBalanceInUSD
函数是在合约 MasterChefBaseStrategy
中,其合约地址为:https://ftmscan.com/address/0xdbc07e219ba0cb5fddcd0fa0c5cc1eddfb77e082#code
investedBalanceInUSD
函数返回的是 getUSDBalanceFromUnderlyingBalance
函数,在 getUSDBalanceFromUnderlyingBalance
函数中可以发现合约使用两个代币的数量进行计算,而前面攻击者由于闪电贷存入了大量的 USDC,从而使得最终的 _amount
的值也变大了。再次回到 getSharePrice
函数中就可以发现,_sharePrice
也会相应变大。
流程第六步使用了 withdraw
函数。
在 withdraw
函数中我们发现同样调用了 getSharePrice
计算 OShare 价格,在该阶段为 1136563707735425848
,OShare 的价格的确变大了,而最终的取款数量是通过内部调用 _withdraw
函数计算得到的。
_withdraw
函数中最终取款的值为 _realWithdraw
,而 _realWithdraw = _toWithdraw.mul(uint256(10)**uint256(ERC20(_underlying).decimals())).div(uint256(10)**uint256(decimals()));
,所以我们再看到 _toWithdraw
,发现其值同样是由 balanceWithInvested
计算得到的,所以这就会导致最终将 OShare 兑换成 USDC 变多。
总结一下:
USDC↑->amount↑->getUSDBalanceFromUnderlyingBalance(underlyingBal)↑->investedBalanceInUSD()↑->balanceWithInvested()↑->_sharePrice↑
USDC↑->amount↑->getUSDBalanceFromUnderlyingBalance(underlyingBal)↑->investedBalanceInUSD()↑->balanceWithInvested()↑->toWithdraw()↑->realWithdraw()↑
后续处理
针对此次攻击,OneRing Finance 团队采取了四种补救措施,包括暂停保险库,分析、调试、修复漏洞,通过财政库还款,以及提供漏洞赏金。
-
保险库状态:保险库已暂停,OneRing Finance 团队正在努力重新设置。
-
分析、调试、修复:OneRing Finance 团队已工作了很多小时,来修复允许黑客执行这次攻击的问题,团队已与许多合格开发者、协议合作,以查漏协议中所有的代码,协议有漏洞是完全出乎意料的,甚至对一些高级开发人员来说也是如此,因为他们以前审查过 OneRing Finance 的代码。
-
通过协议财政部还款:该团队正在制定一项计划,为受影响的人提供具体的中长期还款计划。
-
赏金:团队将提供被盗资金的 15% 以及 100 万 RING 代币,作为黑客返还资金的赏金。
总结
该次攻击事件是由于项目使用实时储备量来计算价格导致攻击者通过闪电贷借出大量 USDC 并存入使得储备量增大拉升 OShare 价格造成差值从而获利。