数。可以用以下公式表示: 其中: • 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。 来源:金色财经lg...