简要
因为前面分析的 Daoswap EXP 的攻击者是SpaceGodzilla Exploiter,说明这个攻击者之前还攻击过SpaceGodzilla。通过调用栈分析,发现攻击合约的结构大致相似,趁此机会,就一并分析下漏洞原理。
SpaceGodzilla同时具有代币和AMM的功能,黑客利用了其中合约中没有使用过的函数,通过扰乱LP中的兑换比例,从而获取了暴利。
漏洞说明
合约分析
1 | function swapAndLiquifyStepv1() public { |
在源代码中存在一个swapAndLiquifyStepv1(),直接向池子中添加流动性。合约中还存在一个正常的方法swapAndLiquify,所以看起来,swapAndLiquifyStepv1()用来向LP流动池中添加流动性,本只应该在向LP池转账时,应该设置成只能internal调用。但在实际代码中,却设置成了public属性,导致黑客swapAndLiquifyStepv1后,导致LP流动池中token数量混乱,产生了套利空间。
漏洞分析
既然知道了漏洞合约中存在问题的函数,下面以实际的攻击交易来进行说明。
0x7f183df11f1a0225b5eb5bb2296b5dc51c0f3570e8cc15f0754de8e6f8b4cca4
整个漏洞的攻击过程如下:
- 从大量不同的Pair中通过闪电贷和交换等获得了295.2万的USDT,此为初始的攻击资本
- 将295.2万的USDT,在GodzillaUSDT的流动池中换出73,775,430,786,944,730,258,898,675,433,018个Godzilla币
- 调用
swapAndLiquifyStepv1漏洞函数,将LP流动池中的Godzilla与USDT的兑换比例完全打乱 - LP流动池中的比例已经打乱,此时用使用71,562,167,863,336,388,351,131,715,170,010个Godzilla币兑换出297.8万的USDT,净套利2.6万USDT
通过调用栈,对具体的调用过程进行详细分析。
第一步,通过从不同Pair中进行闪电贷
第二步,获得当前LP中两个Token的数量
- Token0,SpaceGodzilla(
0x2287C04a15bb11ad1358BA5702C1C95E2D13a5E0)中的数量是,76041697635825849047705725848735 - Token1,BUSD(
0x55d398326f99059fF775485246999027B3197955)中的数量是,90478604689102338898952
此时,SpaceGodzilla和BUSD之间的比例是 840438442.8464187:1
K = X*Y,当前的K是 6880146700280134903645644790330265837423480505602025720
第三步,计算当前攻击合约中的USDT的数量
当前攻击合约通过闪电贷的方式,最终获得了 2952797 BUSD
第四步,swap特定数量的SpaceGodzilla
通过调用栈分析,攻击合约通过LP兑换出73775430786944730258898675433018数量的SpaceGodzilla。
兑换出来的SpaceGodzilla,是经过特定计算的。
攻击合约的BUSD 2952797730003166405412733
- Token0,数量是,76041697635825849047705725848735
- Token1,数量是,90478604689102338898952
兑换前后,需要满足的公式是:XY=k,(X-DX)(Y+DY)=K
当前条件是:已知X,Y,DX,就可以计算出DY,即 73775430786944730258898675433018
所以兑换出来的 SpaceGodzilla 数量就是 73775430786944730258898675433018
第五步,调用swapAndLiquifyStepv1
调用swapAndLiquifyStepv1,将SpaceGodzilla合约中的SpaceGodzilla代币和BUSD全部放入到Pancake中提供流动性
第六步,重新计算当前LP中两个Token的数量
当攻击合约通过swapAndLiquifyStepv1之后,因为提供了流动性,所以LP中的两个代币的数量发生了改变。
- Token0,SpaceGodzilla(
0x2287C04a15bb11ad1358BA5702C1C95E2D13a5E0)中的数量是,2288901594081170758102038305061 - Token1,BUSD(
0x55d398326f99059fF775485246999027B3197955)中的数量是,3073671601005728817436539
此时,SpaceGodzilla和BUSD之间的比例是 744679.9434696357:1(相比之前的840438442.8464187:1变化巨大)
K = X*Y,当前的K是 7035331827224036947372569921877688319008790342490023879
相比没有提供流动性之前,K值变大了。
第七步,将攻击合约中的SpaceGodzilla全部转账到LP中
攻击合约将在第四步中兑换出来的73775430786944730258898675433018的SpaceGodzilla,全部转账到了LP中。可以看到实际的转账数量是71562167863336388351131715170010。数量变少了。这个是因为Godzilla _transfer过程中会收取3%的takeFee。具体代码如下所示:
1 | function _transfer( |
两个数字之间的差额就是被Godzallia合约收走的takeFee,这个takeFee是在_transfer中被收取的。
第八步,通过LB兑换处USDT
最终兑换出来 2978176 BUSD,攻击者通过闪电贷贷出了2952797 BUSD,最终盈利是 25379 BUSD
兑换出来的 BUSD 也是特定计算的。
攻击合约的SpaceGodzilla 73775430786944730258898675433018
- Token0,数量是,2288901594081170758102038305061
- Token1,数量是,3073671601005728817436539
兑换前后,需要满足的公式是:XY=k,(X-DX)(Y+DY)=K
当前条件是:已知X,Y,DX,就可以计算出DY,根据公式X’Y’=K’ , (X’+ DX)(Y’-DY)=K’, 求DY
计算得到,DY 是 2952797730003166405412733
所以兑换出来的 BUSD 数量就是 2952797730003166405412733
漏洞复现
数学原理
有一个很关键的问题,是不需要重复攻击者的步骤,使用任意的BUSD进行攻击,都可以获得盈利呢?结果显然不是的,可以看到整个攻击过程还是涉及到对于AMM机制的理解和计算。整个攻击背后的数学原理如下:
降低分析复杂度,不考虑Dex的交易过程中的手续费磨损同时也要确保token再转账的过程中没有额外的手续费:
正如这篇文章中的最后的结论,因此,这套攻击手法还是要看LP的初始状态以及在攻击过程中的一些参数的配合,才能达到套利的目的,不然自己会被反撸。
总结
参考
https://learnblockchain.cn/article/4396
https://learnblockchain.cn/article/4395
https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/SpaceGodzilla.exp.sol