原题目要求 GuessTheSecretNumberChallenge 合约的 ether 余额为 0。而调用 guess 并输入答案,如果答案的 hash 等于 answerHash 即可转移 ether 出去。
安装 Foundry
根据Foundry 官方文档配置好运行环境。
安装 Rust
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
并根据提示继续操作。
获取 secret number
$ cd WTF-CTF $ cargo run --bin get_hash keccak256(170): "db81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365"
即获得 secret 为 170。具体 rust 代码在 get_hash
运行测试
$ cd WTF-CTF $ forge test -C src/Capture_the_Ether/Lotteries/Guess_the_secret_number -vvv
因为 guess 的参数是 uint8 类型,因此解空间大小为 256,我们可以在链下暴力破解:
fn main() { for i in 0..=255 { let mut hasher = Keccak256::default(); hasher.update(&[i]); let bytes_i = hasher.finalize().to_vec(); let hex_i = hex::encode(bytes_i); if hex_i.contains("db81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365") { println!("keccak256({}): {:?}", i, hex_i); } } }
通过遍历 0-256 的 hash,如果有等于 answerHash 的,输出结果。
在测试合约中的 setUp,我们先给 hacker 转入 1 ether 用于后续调用 guess,然后创建 GuessTheSecretNumberChallenge
function setUp() public { payable(hacker).transfer(1 ether); guessTheSecretNumberChallenge = new GuessTheSecretNumberChallenge{value: 1 ether}(); }
在测试合约的 testGuessTheSecretNumber 中,我们调用 guess,设置参数为 170,并携带 1 ether 就能成功完成挑战
vm.startPrank(hacker); guessTheSecretNumberChallenge.guess{value: 1 ether}(170); vm.stopPrank();