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 ownerof 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.originin 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.originis dangerous for access control
- Difference between msg.senderandtx.origin
- Why authentication should rely on msg.sender
- How to craft simple exploit contracts
Concepts Covered
| Concept | Description | 
|---|---|
| tx.originvsmsg.sender | tx.originis always the original EOA;msg.senderis 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 |