Table of contents
This challenge again is fairly simple and teaches us a new concept. As usual, I will break down the concepts and give you all the resources and tools to solve the problem on your own and then give a solution in case you are stuck.
Objective: Claim ownership of the contract to complete this level.
Background
If you have used solidity in any capacity whatsoever, you must have come across something called msg.sender
and tx.origin
. They are special variables of type address in the global namespace. msg.sender
gives the address of the sender of the current call chain whereas tx.origin
gives the sender of the transaction.
For Ex: In a simple call chain A->B->C->D, Inside D msg.sender
will be C, and tx.origin
will be A.
Note: tx.origin
must be an EOA and can never be another contract.
Problems and Code of Importance
The code and solution are extremely small and easy to understand.
The only function that can update the owner
is the changeOwner()
function, so naturally that should be the focus of the attack.
function changeOwner(address _owner) public {
//only changes the owner if the call is not from original EOA.
if (tx.origin != msg.sender) {
owner = _owner;
}
}
Now, how to exploit this.
Let's consider the following case:
Case 1: Aditya(EOA) calls Telephone.changeOwner(Bajaj)
tx.origin
== Aditya(EOA)msg.sender
== Aditya(EOA)
The owner will not change.
Case 2: Aditya(EOA) calls ContractExploit.something()
which further calls Telephone.changeOwner(Bajaj)
Here, Inside Telephone.changeOwner
tx.origin
== Aditya(EOA)msg.sender
==ContractExploit.something()
Hence, the owner will be changed to our desired address.
Thus, we need to only create a middle contract between the EOA and the Telephone contract.
Solution
Open up Remix and create two files, Telephone.sol and Exploit.sol
Copy the ethernaut code in Telephone.sol
Add the below code to your Exploit.sol (Explanation in comments)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./Telephone.sol"; contract Exploit { Telephone telephone = Telephone("level instance without quotes"); //intializes a variable telephone and stores the ethernaut instance. function exploit() public { telephone.changeOwner(msg.sender); } //exploit calls the changeOwner function in the telephone contract. }
To understand what's happening here, essentially from the perspective of
Exploit.exploit()
, We are themsg.sender
andtx.origin
but from the perspective oftelephone.changeOwner()
we aretx.origin
butExploit.exploit()
ismsg.sender
hence, fulfilling our condition.Compile and Deploy both contracts in the Injected Web3 Metamask Env.
Call the exploit() function.
Check on the console whether you are the owner of the instance.
Submit Instance.
Here are additional resources, to understand tx.origin
attacks. Also as a rule you should never use tx.origin
in your contract unless absolutely required. It might get deprecated in a future update, and opens up the contract to potential phishing attacks.