pSeudoEth@priceManipulation
pSeudoEth
是一种ERC20代币,他的balanceOf()
并不是恒定的,随着市场变动。有黑客发现他实现的逻辑有问题,然后通过uniswap的池子进行价格操纵,拿走了池子中的pSeudoEth
。
- 时间:2023-10-08 10:20:59 (UTC)
- 损失金额:1.43 WETH (~$2.3k)
交易
- 攻击事件hash:0x4ab68b21799828a57ea99c1288036889b39bf85785240576e697ebff524b3930
- 黑客地址:0xea75AeC151f968b8De3789CA201a2a3a7FaeEFbA
- 黑客用来攻击的合约地址:0xf88D1D6D9DB9A39Dbbfc4B101CECc495bB0636F8
- 池子:0x2033B54B6789a963A02BfCbd40A46816770f1161
- 代币地址(未开源):0x62aBdd605E710Cc80a52062a8cC7c5d659dDDbE7
资金流向
攻击过程
攻击详细分析
从攻击过程可以看出,闪电贷只是用来获取启动资金。黑客就做了三个操作:WETH换成pETH,不断调用池子的skim()
方法,pETH换成WETH,做完这三个操作,手头上的WETH就变多了,还款闪电贷获利。
不断的调用skim()
就可以实现价格操纵,我们来看看skim()
是干啥的:将池子中比_reserve
多的币发给to
地址,这个是用来让池子正常运作的,因为uniswap的swap()
会比较balanceOf()
和_reserve
来维持k值不变,在有人“捐赠”代币到池子就会使池子暂时DoS,因此有这个方法将多余的代币转走。
这个方法任何人都可以调用,只要池子有多余的代币,就可以拿走,当然普通人是搞不到的,因为很多机器人实时在监控着池子
1 | // force balances to match reserves |
可以看出,调用了skim()
之后会调用token的transfer()
。但是因为pETH的合约没有开源,我们无法查看他的transfer()
的具体实现。
但是,其攻击逻辑和这篇文章的如出一辙。我们可以推断,这个pETH的transfer()
调用会影响类如_totalSupply
这种变量,balanceOf()
受到这个变量的影响,价格会波动!并且其transfer()
没有检验发送者和接收者的地址,则可以不断调用transfer()
使得_totalSupply
无限制的增大,进而使得我们拥有的pETH增多。
但是,池子中的_reserve
不变,则币对价格不变,我们手头上的pETH经过balanceOf()
获得的余额增多,则我们可以用pETH在池子中换取更多的代币。类似的原理可以查看这个文章。
复现
1 | // SPDX-License-Identifier: UNLICENSED |
建议
- 如果代币的balanceOf()是魔改的,余额会随着市场而变动,那么需要非常注意实现!其中一种漏洞就是transfer()发送者和接收者没有校验不能相同
- 魔改的代币需要时刻注意