获得Preservation合约的owner权限
根据Foundry 官方文档配置好运行环境后,于本项目下执行下列命令:
$ cd WTF-CTF
$ forge test -C src/Ethernaut/Preservation -vvvvv
Preservation 合约使用 delegatecall 时只会使用 LibraryContract合约的代码逻辑。
LibraryContract合约寻找变量时会按照 LibraryContract合约变量所在的插槽寻找 Preservation合约对应插槽中的变量。
首先调用 Preservation合约的 setFirstTime函数,函数参数为:Attack合约的地址。此时,Preservation合约中的timeZone1Library变量就修改为了Attack合约的地址。
Preservation(preservation).setFirstTime(uint256(uint160(attack)));
然后再调用 Preservation合约的 setFirstTime函数,函数参数为:我的账户地址。因为Attack合约寻找变量时会按照 Attack合约变量所在的插槽寻找 Preservation合约对应插槽中的变量。从而改变owner账户的地址。
Preservation(preservation).setFirstTime(uint256(uint160(address(this))));
所以,在使用代理调用delegatecall时,需要保证proxy合约和implementation合约的内存插槽不要有冲突,如果proxy合约中有变量,implementation合约中就不要使用proxy合约中变量占据的插槽位置。另一个解决办法是proxy合约存储变量时尽量指定插槽位置,不使用默认从0开始的插槽排布
assembly {
param.slot := slot
}
具体,可以查看ERC-1967: Proxy Storage Slots或openzeppelin对代理合约的实现和文档,另外ERC-2535: Diamonds, Multi-Facet Proxy 可以同时代理多个合约。