来源:零时科技
事件背景
2023-11-13 18:51:57 (UTC) 链上发生一笔针对 OKC Token 的攻击事件,黑客通过存在缺陷的MinerPool获取了及时收益
黑客攻击交易:
0xd85c603f71bb84437bc69b21d785f982f7630355573566fa365dbee4cd236f08
黑客攻击合约1:
0xD5d8c2fd8A743A89BC497B2F180C52d719a007B9
黑客攻击合约2:
0x617432Fc98c1fFaAB62B8cB94Cef6D75ABD95598
黑客攻击合约3:
0x28e7c8337373C81bAF0A4FE88ee6E33d3C23E974
攻击者地址:
0xbbcc139933D1580e7c40442E09263e90E6F1D66D
漏洞合约:
0x36016C4F0E0177861E6377f73C380c70138E13EE (MinerPool)
攻击分析
通过对链上交易数据分析,我们对其交易进行整理归纳。
黑客首先通过多个闪电贷进行对攻击资金的筹集,共借款 2,753,399 USDT Token。
随后立即将其中 130,000 USDT Token 兑换为一个最小单位的 USDT Token 和 27,264 OKC Token 。
由于 PancakeSwapv2 使用的是AMM(恒定函数做市商),在该模型中,交易池中的两种代币的数量乘积是一个常数。可以用以下公式表示:
其中:
• x是交易池中第一种代币的数量。
• y是交易池中第二种代币的数量。
• k是一个常数,表示池中两种代币数量的乘积。
当一个交易者想要用一种代币兑换另一种代币时,他们会增加交易池中一种代币的数量(dx),同时减少另一种代币的数量(dy),以保持的不变性。这个过程会改变代币的相对价格。
假设交易者想要用代币A兑换代币B,那么在交易前后,恒定乘积公式应该保持成立:
由于k是不变的,这意味着x·y在交易前后是相同的。但是,因为交易者增加了代币A的数量(dx)并且减少了代币B的数量(dy),这将导致代币B的价格上升。
代币的即时价格可以通过计算池中两种代币数量的比率来得出。如果代币A是x,代币B是y,则代币B相对于代币A的价格是y/x。在交易之后,代币B的数量减少了,代币A的数量增加了,所以新的价格变成了(y-dy)/(x+dy) 。由于分子减小而分母增大,这个比率变小了,意味着代币B的价格上升。
在大额交易的情况下,dx和dy可能非常大,这会导致价格的显著变动。这是因为为了保持k的不变,必须从池中移除大量的dy来补偿dx的增加。这种大额交易对池中代币数量的巨大影响导致了价格的显著变化。
故攻击者通过使用大额闪电贷资金进行买入OKC,导致 OKC 数量减少,从而拉高了 OKC Token 的价格,将 1 OKC = 0.3 USDT 的价格提升到 1 OKC = 68.9 USDT。
后攻击者创建了两个合约地址,并分别向两个地址中发送了 0.01 OKC 和 0.0001 USDT 和 一个最小单位的OKC。
然后黑客使用主要的攻击合约对 PancakePair_USDT_OKC 池子添加流动性操作,得到约 225,705 个LP Token。
随后将 LP Token 全部转移至 攻击者创建的 0x28e7c8337373C81bAF0A4FE88ee6E33d3C23E974 攻击合约中随后立即调用漏洞合约中的 processLPReward 函数,对合约内存储的 lpHolder 地址进行奖励分配。此处虽然攻击者的操作只是向 MinerPool 合约转账,但是该合约在接受转账的回调函数中调用了processLPReward 函数。
根据下图,我们得知攻击合约 0x28e7c8337373C81bAF0A4FE88ee6E33d3C23E974 在奖励领取中获得了 77,890 个 OKC Token。
然后攻击者转出攻击合约 0x28e7..E974 的的 LP Token 并将其销毁移除流动性,获得了 1,884,223 USDT Token,以及 27,264 OKC Token。
并转移出另外两个攻击合约中的所有的Token至主要攻击合约
0xD5d8c2fd8A743A89BC497B2F180C52d719a007B9 ,分别为:272 个,77,890 个 OKC Token。
黑客将所有的OKC ,约 104,610 OKC Token 兑换为约 136,518 USDT Token,此时黑客总持有约 2,759,918 USDT Token。
最后,黑客归还所有闪电贷的本金和利息,最终剩余约 6,268 USDT Token,并全部转移至攻击者地址
0xbbcc139933D1580e7c40442E09263e90E6F1D66D。
漏洞分析
通过攻击分析得知,黑客主要获利资金是由 MinerPool 合约中 processLPReward 函数,该函数逻辑主要是获取 lpHolder 并根据其 LP 数量直接按比例进行奖励。
我们可以看一下攻击者创建的第三个攻击合约中的执行逻辑:
从该处逻辑可以看出addHolder函数中使用了 extcodesize() 来计算地址当前的大小,用来判断地址是否是合约地址,但是在攻击者通过 CREATE2 创建合约,由于合约在初始化时,该地址大小依然为0,从而来攻击者在其构造函数中调用该函数绕过其中的合约调用限制。
其在转账时调用了 addHolder 函数将 合约地址添加到了 lpHolder 名单,所以该合约才可以通过领取奖励获取及时的OKC奖励。
根据对 processLPReward 函数代码逻辑进行分析,可以从下图中看出,虽然设置了领取奖励的锁定时间,但是未设置 LP 持有时间进行校验或限制,导致黑客通过闪电贷获取大量临时资金,并兑换为LP ,并在领取奖励后立即销毁。
总结
简单来说,黑客通过闪电贷借了大量USDT,并兑换大量OKC,从而拉高了OKC的价格。并且由于OKC项目未设置LP奖励发放的锁仓要求,所以黑客在获取奖励后立刻撤回流动性,从而获取了项目方发放的流动性提供者的奖励。并将OKC项目方奖励的OKC Token出售。最终获利 6,268 USDT。