Ethernaut#4 - Telephone
Ethernaut #4 - Telephone
Claim ownership of the contract below to complete this level.
Things that might help
- See the “?” page above, section “Beyond the console”
1 | // SPDX-License-Identifier: MIT |
Contract Breakdown
Let us breakdown the contract to understand what each function and piece of code does. Mere looking at it we can see the code consists of a single contract Telephone compiled with solidity version ^0.8.0. This is a pretty short contract.
Variables
1 | address public owner; |
A single public variable owner of type address.
Constructor
1 | constructor() { |
The constructor sets the owner to msg.sender, the deployer of the contract.
Functions
1 | function changeOwner(address _owner) public { |
The changeOwner function takes in an address _owner and sets the contract owner to that address, but only if tx.origin is not equal to msg.sender. This is the only function in the contract and it is the key to the whole challenge.
Solution
To understand the vulnerability, we need to understand the difference between tx.origin and msg.sender.
tx.originalways refers to the externally owned account (EOA) that originally initiated the transaction, whilemsg.senderrefers to the immediate caller of the current function.
When we call a contract function directly from our wallet, tx.origin and msg.sender are the same (both are our wallet address). But when our wallet calls Contract A, and Contract A calls Contract B, inside Contract B tx.origin is still our wallet but msg.sender is Contract A.
So to pass this challenge, we simply need to call the changeOwner function through an intermediary contract. This way tx.origin will be our wallet address and msg.sender will be the intermediary contract’s address, making them different and satisfying the condition.
1 | // SPDX-License-Identifier: MIT |
Steps to pass:
- Copy the
HackTelephonecontract to Remix and compile it. - Deploy it with the Telephone instance address from Ethernaut.
- Call
hackOwnershipwith our wallet address as the argument. - Since the call goes through
HackTelephone,tx.origin(our wallet) !=msg.sender(HackTelephone contract), and ownership is transferred to us.
Never use
tx.originfor authorization. It is vulnerable to phishing attacks where a malicious contract tricks a user into calling it, and then usestx.originto impersonate the user. Always usemsg.senderfor access control.