头条
攻击的本质是在铸造份额代币是精度计算出现了问题,使得关键变量 index 值被操纵,并且未详细考虑代币精度的四舍五入问题。
撰文:SharkTeam
北京时间 2023 年 11 月 11 日,Raft 遭受闪电贷攻击,项目方损失 360 万美元,但随后攻击者几乎将全部 ETH 转入黑洞地址销毁,不知是刻意还是失误。
SharkTeam 对此事件第一时间进行了技术分析,并总结了安全防范手段,希望后续项目可以引以为戒,共筑区块链行业的安全防线。
攻击者地址:
0xc1f2b71A502B551a65Eee9C96318aFdD5fd439fA
攻击合约:
0x0A3340129816a86b62b7eafD61427f743c315ef8
0xfdc0feaa3f0830aa2756d943c6d7d39f1d587110
0x011992114806e2c3770df73fa0d19884215db85f
被攻击合约:
0x9ab6b21cdf116f611110b048987e58894786c244
0xd0db31473caad65428ba301d2174390d11d0c788
攻击交易:
0xfeedbf51b4e2338e38171f6e19501327294ab1907ab44cfd2d7e7336c975ace7
攻击流程:
(1) 攻击者(0xc1f2b71A)通过闪电贷借取 6000 枚 cbETH。
(2)随后向攻击合约(0x9ab6b21c)转移 6001 枚的 cbETH。
(3)攻击者(0xc1f2b71A)调用 liquidate 函数(使用预先创建的仓位),目的是通过逻辑漏洞改变 storedIndex 变量的值。
(4)攻击者(0xc1f2b71A)随后调用 60 次 managePosition 函数,每次使用 1wei 的 cbETH 换取 1wei 的 rcbETH-c,总计获得 60wei。
(5)攻击者(0xc1f2b71A)通过上一步获得的 60wei rcbETH-c 加上已更改的 storedIndex 变量,使攻击合约(0x0A334012)目前的 rcbeth-c 的余额到了 10050 枚。
(6) 攻击者(0xc1f2b71A)调用 managePosition 函数使用 rcbETH-c 换回 cbETH。
(7)然后调用 managePosition 函数凭借剩下的 rcbETH-c 代币借出了 6.7m 枚 R 代币。
(8)将获得的 R 代币兑换为 sDai,Dai,USDC 等代币
(9)攻击者(0xc1f2b71A)最后将所有代币兑换为 WETH 并提取成 ETH 发给零地址。
(10)最后攻击者(0xc1f2b71A)偿还闪电贷后,仅获得 7ETH。
攻击的本质是在铸造份额代币是精度计算出现了问题,使得关键变量 index 值被操纵,并且未详细考虑代币精度的四舍五入问题。
rcbETH 中,balanceOf 函数不仅通过原生的 balanceOf 计算余额,还与 storedIndex 相乘后才是最终的余额。
攻击者(0xc1f2b71A)在第二步中预先将 6001 枚 cbETH 转入了 InterestRatePositionManager 合约中,随后在 liquidate 函数中,将 cbETH 的 setIndex 函数传参设置为了当前的 cbETH 余额。
导致在 setIndex 函数中最后计算得到的 storedIndex 变量变大了上千倍。
并且在 cbETH 合约中,mint 函数使用 divUp 函数来进行铸造,在 divUp 模式中,遵循向上取整原则,只要 a 不等于 0,那么最终计算出的结果最小也会是 1。
所以在第四步中攻击者只用了一 wei 的 cbETH 就换到了 1wei 的 rcbETH-c,而由于 storedIndex 变量变大了上千倍,所以 1wei 的 rcbETH-c 最终呈现出来的余额也会变大上千倍。导致攻击者获得足够的 rcbETH-c 后不仅可以把 cbETH 给换回来,还能借出大量的 R 代币。
针对本次攻击事件,我们在开发过程中应遵循以下注意事项:
(1)在进行份额币铸造时,严格校验币和币之前的精度问题。
(2)开发代币精度时,慎重考虑取整情况,是否需要向上取整。
(3)项目上线前,需要向第三方专业的审计团队寻求技术帮助。