Skip to content

Latest commit

 

History

History
74 lines (44 loc) · 3.76 KB

040.md

File metadata and controls

74 lines (44 loc) · 3.76 KB

Teeny Myrtle Tardigrade

High

A spender can front-run the BoostStablecoin::approve function to steal funds from a Boost token holder

Summary

approve function that BoostStablecoin contract inherited from the openzeppelin ERC20BurnableUpgradeable contract, in line 14 below, allows a token holder to give another address (a wallet or a contract) permission to spend a specified amount of his Boost tokens on his behalf. The approved spender just need to call BoostStablecoin::transferFrom after the approval to spend the allowed amount.

https://github.com/sherlock-audit/2024-10-axion/blob/main/liquidity-amo/contracts/BoostStablecoin.sol#L14

However, If a Boost token holder decides to change an unspent approved amount for a spender, the second approval transaction can be front-run to steal the holder's funds. Please see the Attack Path section below for the detailed steps on how the exploit can be carried out. The attack vector is also explained in the link below.

https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit?tab=t.0

Root Cause

The BoostStablecoin::approve function does not check if the current spender has unspent allowance before changing the approved amount to the new value.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

  1. Alice, a BoostStablecoin holder, approves Bob (the spender) to spend 100 Boosttokens from her wallet;
  2. Before Bob spends the approved 100 Boosttokens, Alice changed her mind and decides to reduce the approval for Bob from 100 to 50 Boosttokens;
  3. Alice sends another transaction to approve 50 Boosttokens for Bob to the mempool;
  4. Bob sees this new transaction in the mempool and decides to front-run it;
  5. Bob sends a transaction with a higher gas fee to spend the initially approved 100 Boosttokens;
  6. Bob's transaction was executed ahead of Alice's transaction in the mempool and he successful spends the 100 Boosttokens;
  7. After Alice's 50-token approval transaction in the mempool was executed, Bob immediately sends another transaction to spend the 50 Boosttokens just approved.

At the end, Bob successfully spends 150 Boosttokens from Alice' wallet but Alice intention was to allow him spend only 50 Boosttokens.

Impact

Malicious users will be stealing funds of other users.

PoC

No response

Mitigation

Below is how USDT approve function was implemented to minimize the impact of such exploitation;

https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#code

    // -------------- USDT approach -------------------
    function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {

        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender, 0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));

        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
    }

So, if a Boost token holder wants to change an approved amount for a spender, he has to zero the approval first before changing the approved amount to another value. This way the Boost token holder will have sufficient time to check his balance before making a new approval. With this mitigation, Bob, in the illustration above, would have been able to spend only 100 Boost tokens initially approved by Alice if he had front-run the second approval. Alice would have zeroed Bob's approval with the second transaction and probably notice 100 tokens have been spent already from her balance.