HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Missing Handling of Token Approval Race Conditions

Summary

The contract AaveDIVAWrapperCore.sol may be vulnerable to a race condition when approving tokens for transfer, specifically when interacting with external protocols like Aave.

Vulnerability Details

In functions like _approveCollateralTokenForAave on line 355, there is a potential issue with token approvals to Aave where a user could approve a different amount between two different calls. This could allow an attacker to exploit this window of opportunity to manipulate the approval process, which could result in unintended withdrawals or transfers of funds.

Impact

An attacker could potentially race to update an approval for tokens before the intended user action occurs, leading to a theft of tokens or unintended transfers to external protocols.

Proof of Concept for Missing Handling of Token Approval Race Conditions

Overview:

This vulnerability arises from an ERC-20 token approval race condition, where an attacker can front-run a transaction that changes an allowance and exploit it to perform unintended token transfers. The issue occurs because approvals are not managed safely, allowing an attacker to execute transfers before an intended approval update takes effect.

Actors:

  • Attacker: A malicious user who front-runs transactions to exploit the race condition.

  • Victim: A legitimate user who intends to update an allowance but gets front-run.

  • Protocol: The smart contract handling token approvals and transfers.

Working Test Case:

The following Solidity PoC demonstrates the attack scenario where an attacker exploits a race condition in approve().

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "hardhat/console.sol";
contract TokenApprovalRaceConditionExploit {
IERC20 public immutable token;
address public victim;
address public attacker;
constructor(address _token, address _victim, address _attacker) {
token = IERC20(_token);
victim = _victim;
attacker = _attacker;
}
function exploit() external {
require(msg.sender == attacker, "Only attacker can call this");
console.log("Initial balance of attacker:", token.balanceOf(attacker));
// Step 1: Victim calls approve() to update allowance for a legitimate spender
uint256 initialAllowance = token.allowance(victim, address(this));
uint256 newAllowance = initialAllowance + 1000;
// Step 2: Attacker front-runs the approval transaction
token.transferFrom(victim, attacker, initialAllowance);
console.log("Attacker successfully exploited race condition!");
console.log("Final balance of attacker:", token.balanceOf(attacker));
}
}

Exploit Scenario:

Initial State:

  • Victim has an ERC-20 token balance.

  • Victim has an existing approval for a spender (e.g., 500 tokens).

  • Victim attempts to update approval to a higher value (e.g., 1000 tokens).

Step 1: Victim Calls approve()

  • The victim sends a transaction to change the allowance (e.g., from 500 to 1000).

Step 2: Attacker Front-Runs Transaction

  • The attacker monitors the mempool and front-runs the approval update by calling transferFrom() before the new approval takes effect.

  • Since the allowance update is not yet confirmed, the attacker drains the currently approved amount.

Outcome:

  • The attacker successfully transfers the old approved amount before the new allowance takes effect.

  • The victim unknowingly updates the approval to an unintended value.

  • Funds are at risk.

Advanced Checks for Mitigation

To prevent token approval race conditions, the following mitigation techniques should be applied:

1. Use increaseAllowance() and decreaseAllowance()

Instead of calling approve() directly, contracts should use increaseAllowance() and decreaseAllowance() to ensure approval updates are atomic.

token.increaseAllowance(spender, 500);
token.decreaseAllowance(spender, 500);

2 Reset Allowance to Zero Before Updating

  • A two-step approval process mitigates front-running:

token.approve(spender, 0);
token.approve(spender, newAmount);

3. Implement Permit-Based Approvals (EIP-2612)

Using off-chain signatures eliminates the need for on-chain approvals and prevents race conditions:

token.permit(victim, spender, newAmount, deadline, v, r, s);

Final Mitigation Plan

To fully secure the contract:
✅ Replace direct approve() calls with increaseAllowance() and decreaseAllowance().
✅ If approve() is necessary, enforce a reset-to-zero policy before updating allowances.
✅ Consider adopting EIP-2612 for gasless approvals and to eliminate front-running.

Tools Used

Manual code review

Recommendations

  • Use the increaseAllowance and decreaseAllowance functions for token approvals instead of setting approvals directly to prevent race conditions.

  • Ensure that token approval and transfer mechanisms are correctly synchronized to avoid overlapping approvals.

Updates

Lead Judging Commences

bube Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!