慢雾:Cellframe 被黑简析

据慢雾安全团队情报,2023 年 6 月 1 日,Cellframe 遭到闪电贷攻击,Cellframe ERC20 v2 价格下跌 41.2%。慢雾安全团队第一时间介入分析,并将结果分享如下:

相关信息

攻击者地址:

0x2525c811EcF22Fc5fcdE03c67112D34E97DA6079

攻击者合约地址:

0x1e2a251b29e84e1d6d762c78a9db5113f5ce7c48

攻击交易:

0x943c2a5f89bc0c17f3fe1520ec6215ed8c6b897ce7f22f1b207fea3f79ae09a6

攻击者添加 LP(OLD) 交易:

0xe2d496ccc3c5fd65a55048391662b8d40ddb5952dc26c715c702ba3929158cb9

前置信息

此次攻击中出现多个新旧合约,我们将使用 LpMigration 合约中新旧合约的参数名作为本次攻击分析中的合约名。

address OLD_CELL: 0xf3E1449DDB6b218dA2C9463D4594CEccC8934346

address LP_OLD: 0x06155034f71811fe0D6568eA8bdF6EC12d04Bed2

address CELL: 0xd98438889Ae7364c7E2A3540547Fad042FB24642

address LP_NEW: 0x1c15f4E3fd885a34660829aE692918b4b9C1803d

具体细节分析

1. 通过 DODO 的 DPPOracle 闪电贷 1000 个 BNB。

2. 通过 PancakeSwap V3 闪电贷 50 万枚 CELL。

3. 攻击者在 PancakeSwap V2 LP_NEW 池子中 ,将闪电贷来的 50 万枚 CELL Token 全部 swap 为 50 枚 BNB。这时,LP_NEW 池中仅剩 8 枚 BNB,而 CELL 有 55 万枚。

4. 紧接着,攻击者在另外一个 PancakeSwap V2 池子 LP_OLD 池中,将 900 枚 BNB swap 为 OLD_CELL。此时 LP_OLD 中的 BNB 数量为 902 枚,OLD_CELL 仅有 7 枚。

5. 在攻击者将 BNB 兑换成 OLD_CELL 后,我们发现攻击者直接调用 LpMigration 合约的 migrate 函数进行 LP 迁移。奇怪的是,在我们刚刚的分析里,攻击者并没有获取 LP Token 的操作,那么这些 LP Token 又是从哪里来的呢?

6. 于是回到攻击合约,通过攻击合约的前一笔交易可以发现,攻击者在交易中向 LP_OLD 池子添加流动性,获取 LP(OLD) Token。

7. 攻击者对 LP_OLD 池的 LP(OLD) 进行连续 migrate 操作,细节如下:

先调用 migrateLP 函数移除 LP(OLD) 的流动性,并将代币返还给用户。由于池子 LP_OLD 中的 BNB 代币很多,在移除流动性时,计算所获得的 BNB 数量会增加,OLD_CELL 减少。随后,在 LP_NEW 池中,getReserves 获取到 BNB 和 CELL 的数量。由于之前的 swap 操作,LP_NEW 池中的 BNB 数量少,CELL 数量多,所以计算出来的 resoult 值会偏大,使得新计算出的 CELL 的 token1 值也偏大。

8. 然而,原本 LpMigration 合约中又是存在 CELL 代币的,所以攻击者添加流动性所用的 token1 代币 CELL 都来自 LpMigration 合约。随后将计算出的结果添加流动性到 ROUTER_V2 池中。(PS: 这也是为什么在攻击发生后,Cellframe: Deployer 会通过 withdrawCELL() 函数将合约中的 CELL 代币全部取出)

9. 也就是说,攻击者利用 LP_OLD 池中 BNB 多,OLD_CELL 少,通过移除流动性获取到更多的 BNB。而在 LP_NEW 池中 BNB 少,CELL 多的情况下,以少量的 BNB 和 CELL 就可以添加流动性。攻击者通过多次 migrate 操作获利。

10. 最后,攻击者将 LP_NEW 的流动性移除,将 OLD_CELL 在 LP_OLD 池中兑换为 BNB,并在一个新的 CELL-BUSD 池中先兑换成 BUSD 再兑换成 BNB,偿还 FlashLoan,获利 245.522826177178247245 BNB。

总结

此次攻击的核心在于利用流动性迁移计算,攻击者操纵两个不同池子中的流动性致其不平衡,进而套利。