Telephone – Level 4 Walkthrough
Welcome to the Telephone level of the Ethernaut wargame by OpenZeppelin, focused on understanding tx.origin
misuse in access control.
Challenge Objective
You are given a contract with an ownership change function that improperly uses tx.origin
for authentication. Your goal is to:
- Exploit the contract’s flawed use of
tx.origin
- Become the new
owner
of the contract - Submit the instance to complete the level
Prerequisites
Before you begin, ensure the following:
- MetaMask is installed and connected to the same testnet
- You have test ETH (e.g., on Sepolia or Goerli)
- Familiarity with Remix IDE, MetaMask, and browser DevTools
- Access to the Ethernaut Game
Getting Started
1. Load the Level
- Navigate to ethernaut.openzeppelin.com
- Select “Telephone” from the level list
- Click “Get new instance”
- Approve the MetaMask transaction
Once deployed, a contract
object is available in the browser DevTools console.
Vulnerability Background
The Telephone contract uses tx.origin
instead of msg.sender
in its changeOwner()
function:
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
This logic prevents direct calls from the EOA (your wallet), because in such a case tx.origin == msg.sender
.
But if we call this function via another contract, then msg.sender
will be the intermediate contract, and tx.origin
will be your wallet — thus satisfying the condition.
Walkthrough – Step by Step
Step 1: Write the Exploit Contract
Open Remix IDE and create a new file: TelephoneAttack.sol
Paste this code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITelephone {
function changeOwner(address _owner) external;
}
contract TelephoneAttack {
function attack(address target) public {
ITelephone(target).changeOwner(msg.sender);
}
}
This contract:
- Calls
changeOwner()
on the vulnerable contract - Since this is a contract call,
msg.sender ≠ tx.origin
in the target function msg.sender = this contract
,tx.origin = your wallet
, so it passes the check
Step 2: Compile and Deploy the Attack Contract
- Go to the Solidity Compiler tab
- Select version 0.8.x
- Click Compile TelephoneAttack.sol
Then:
- Go to the Deploy & Run Transactions tab
- Set Environment to
Injected Provider - MetaMask
- Make sure the correct wallet/network is selected
- Click Deploy (no constructor args needed)
Step 3: Execute the Attack
- Expand your deployed contract in Remix
- In the
attack()
input box, paste your Telephone contract instance address (from the console:contract.address
) - Click attack()
- Confirm transaction in MetaMask
Step 4: Confirm Ownership
Back in the Ethernaut console:
await contract.owner()
It should now return your address.
Step 5: Submit the Level
Click Submit Instance in the Ethernaut UI and confirm in MetaMask.
🎉 Done! You’ve completed the Telephone level.
💡 What You Learn
tx.origin
is dangerous for access control- Difference between
msg.sender
andtx.origin
- Why authentication should rely on
msg.sender
- How to craft simple exploit contracts
Concepts Covered
Concept | Description |
---|---|
tx.origin vs msg.sender | tx.origin is always the original EOA; msg.sender is the immediate caller |
Access control | Proper ownership checks must use msg.sender |
Contract interaction | Using contracts to spoof call context |
Interface injection | Calling external contracts via interfaces |