Teeny Myrtle Tardigrade
High
A spender can front-run the BoostStablecoin::approve
function to steal funds from a Boost
token holder
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.
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
The BoostStablecoin::approve
function does not check if the current spender has unspent allowance before changing the approved amount to the new value.
No response
No response
- Alice, a
BoostStablecoin
holder, approves Bob (the spender) to spend 100Boost
tokens from her wallet; - Before Bob spends the approved 100
Boost
tokens, Alice changed her mind and decides to reduce the approval for Bob from 100 to 50Boost
tokens; - Alice sends another transaction to approve 50
Boost
tokens for Bob to the mempool; - Bob sees this new transaction in the mempool and decides to front-run it;
- Bob sends a transaction with a higher gas fee to spend the initially approved 100
Boost
tokens; - Bob's transaction was executed ahead of Alice's transaction in the mempool and he successful spends the 100
Boost
tokens; - After Alice's 50-token approval transaction in the mempool was executed, Bob immediately sends another transaction to spend the 50
Boost
tokens just approved.
At the end, Bob successfully spends 150 Boost
tokens from Alice' wallet but Alice intention was to allow him spend only 50 Boost
tokens.
Malicious users will be stealing funds of other users.
No response
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.