• YouTube Channel
  • System Status
  • VS Code Extension
  • Arbitrary transferFrom

    Overview

    What is the Arbitrary transferFrom vulnerability?

    The ERC-20 standard interface specifies a transferFrom() function, which can be used to transfer tokens between two specified accounts. In some implementations, the from address may be vulnerable to attacks that allow anyone to spend another user's token balance.

    As a result, a best practice is to ensure that the from address in a call to transferFrom() is the msg.sender value, particularly when calling an ERC-20 implementation that you did not author.

    A real-world example: undisclosed live ERC-20 contracts

    In 2018, a blockchain security research firm uncovered multiple undisclosed ERC-20 implementations that were vulnerable to the Arbitrary transferFrom vulnerability. These contracts did not properly check the allowance of tokens allotted to the message sender from the from address, allowing users to spend tokens held by any address with a balance.

    Further reading: New allowAnyone Bug Identified in Multiple ERC20 Smart Contracts

    Technical example of vulnerable code

      // SPDX-License-Identifier: Unlicense
      pragma solidity ^0.8.0;
        
      contract VulnerableERC20 {
          mapping(address => mapping(address => uint256)) public allowed;
    
          function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
              require(balances[_from] >= _value);
              require(balances[_to] + _value > balances[_to]);
    
              balances[_from] -= _value;
              balances[_to] += _value;
              allowed[_from][msg.sender] -= _value;
    
              return true;
          }
    
          // additional ERC-20 implementation
      }
    
    

    The transferFrom() function in the VulnerableERC20 contract deducts the _value amount without checking the allowance. This allows anyone to spend the tokens controlled by any account.

    Technical example of how to fix the vulnerability

      // SPDX-License-Identifier: Unlicense
        pragma solidity ^0.8.0;
        
        contract VulnerableERC20 {
            mapping(address => mapping(address => uint256)) public allowed;
    
            function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
                require(balances[_from] >= _value);
                require(balances[_to] + _value > balances[_to]);
                require(allowed[_from][msg.sender] >= _value); // check allowance before deducting _value from _from's balance
    
                balances[_from] -= _value;
                balances[_to] += _value;
                allowed[_from][msg.sender] -= _value;
    
                return true;
            }
    
            // additional ERC-20 implementation
        }
    
    

    In the corrected code example, the allowance is enforced before allowing a third party to transfer tokens on a user's behalf.