Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontrunning Vulnerability in approve() function of ERC20 #725

Closed
c4-submissions opened this issue Sep 14, 2023 · 4 comments
Closed

Frontrunning Vulnerability in approve() function of ERC20 #725

c4-submissions opened this issue Sep 14, 2023 · 4 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working duplicate-145 sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@c4-submissions
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137

Vulnerability details

Impact

The approve() function in the provided ERC20 contract is susceptible to a frontrunning attack. Malicious actors can exploit the timing between an approve() transaction being sent and it getting mined, potentially allowing them to spend more tokens than the token owner intended. If an owner tries to change the allowance of a spender, the spender can spend the original allowance before the new one takes effect, and then spend the new allowance, resulting in a total expenditure exceeding the latest set amount.

Proof of Concept

Consider the following scenario:

  1. Token owner Alice has approved spender Bob to withdraw 1,000 tokens.

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137

    function approve(address spender, uint256 value) external returns (bool) {
        allowance[_msgSender()][spender] = value;

        emit Approval(_msgSender(), spender, value);

        return true;
    }
  1. Alice decides to modify this approval to allow Bob to withdraw only 1,500 tokens. She sends a transaction to set this new allowance.
  2. Bob, monitoring the transaction pool, sees Alice's pending transaction.
  3. Before Alice's transaction is mined, Bob sends a transaction (with a higher gas price for priority) to withdraw the initial 1,000 tokens.
  4. If Bob's transaction is mined before Alice's, he will have withdrawn the 1,000 tokens.
  5. Once Alice's transaction is mined, the allowance is reset to 1,500 tokens, allowing Bob to withdraw an additional 1,500 tokens.
  6. In total, Bob can withdraw 2,500 tokens, even though Alice intended a maximum of 1,500 tokens in the second approval.

Tools Used

Manual

Recommended Mitigation Steps

Before increasing or decreasing the allowance, the contract could require that the current allowance be set to 0. This way, Alice would have to first send a transaction setting the allowance to 0 before setting it to a new value, ensuring Bob cannot front-run in between. The downside is that this requires two separate transactions, which might not be very user-friendly.

https://github.com/code-423n4/2023-09-centrifuge/blob/main/src/token/ERC20.sol#L131-L137

    function approve(address spender, uint256 value) external returns (bool) {
+        require((allowance[msg.sender][spender] == 0), "ERC20: set allowance to 0 first");
        allowance[_msgSender()][spender] = value;

        emit Approval(_msgSender(), spender, value);

        return true;
    }

Alternatively, simply remove approve() since users can always use increaseAllowance() for the same intended purpose.

In your contract, you've provided the increaseAllowance() and decreaseAllowance() functions, which is good. Informing users to prefer these methods over a direct approve() for changing allowances can mitigate the front-running risk.

Assessed type

Timing

@c4-submissions c4-submissions added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Sep 14, 2023
c4-submissions added a commit that referenced this issue Sep 14, 2023
@c4-pre-sort c4-pre-sort added the sufficient quality report This report is of sufficient quality label Sep 15, 2023
@c4-pre-sort
Copy link

raymondfam marked the issue as sufficient quality report

@c4-pre-sort
Copy link

raymondfam marked the issue as duplicate of #145

@c4-judge
Copy link

gzeon-c4 marked the issue as unsatisfactory:
Invalid

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Sep 26, 2023
@c4-judge
Copy link

gzeon-c4 marked the issue as unsatisfactory:
Invalid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working duplicate-145 sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

3 participants