😠General Considerations for ERC777 Reentrancy Vulnerabilities

Overview:

ERC777 tokens introduce new features compared to the more common ERC20 standard, one of which is the hooks mechanism. This feature allows a recipient to register a function (or "hook") that will be automatically called when tokens are transferred to them. While this adds flexibility for certain use cases, it also introduces a significant reentrancy risk if not handled properly. Specifically, the reentrancy attack occurs when an attacker uses the hook mechanism to re-enter the contract during a transfer, potentially exploiting vulnerabilities such as draining rewards or other assets.


Vulnerability: Reentrancy in ERC777 Transfers

In a staking contract that uses ERC777 tokens for rewards, attackers can exploit the lack of protection around reentrancy. If the staking contract does not follow proper safety measures—such as using the checks-effects-interactions pattern—attackers can manipulate the contract’s logic, draining rewards or other assets during the execution of a reward distribution function.


How ERC777 Reentrancy Works:

  1. ERC777 Tokens and Hooks:

    • ERC777 tokens allow recipients to register a hook (a function) that is triggered when tokens are transferred to them. This hook gives the recipient an opportunity to execute additional logic, including reentering the contract that sent the tokens.

  2. Reentrancy Attack:

    • When a staking contract uses ERC777 tokens to distribute rewards, an attacker can take advantage of the hooks by reentering the contract’s functions while the reward transfer is in progress.

    • If the contract does not properly manage its state before transferring rewards (e.g., by clearing reward balances after the transfer), the attacker can repeatedly reenter and claim rewards multiple times, draining the reward pool.


Example of a Vulnerable Function

Here is an example of a vulnerable claimRewards function in a staking contract that uses ERC777 tokens for rewards:

function claimRewards(address user, IERC20[] memory _rewardTokens) external {
    for (uint8 i = 0; i < _rewardTokens.length; i++) {
        uint256 rewardAmount = accruedRewards[user][_rewardTokens[i]];

        // Check for zero rewards
        if (rewardAmount == 0) revert("Zero rewards");

        // Vulnerability: Transferring rewards before clearing accruedRewards
        _rewardTokens[i].transfer(user, rewardAmount);

        // Clear the user's rewards after the transfer (Too late! Vulnerable to reentrancy)
        accruedRewards[user][_rewardTokens[i]] = 0;

        emit RewardsClaimed(user, _rewardTokens[i], rewardAmount);
    }
}    

Vulnerable Aspects:

  • Reentrancy Risk: The reward is transferred to the user before the reward balance is cleared. This allows an attacker to register an ERC777 hook, reenter the contract during the transfer, and repeatedly claim rewards before their balance is updated.

  • ERC777 Hooks: Attackers can use the hook mechanism to reenter the claimRewards function and continuously withdraw rewards in a single transaction.

Last updated