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

Risk in Token Approval Process

Summary

The _approveCollateralTokenForAave function in AaveDIVAWrapperCore introduces a potential front-running vulnerability by failing to reset allowances to zero before increasing them. This exposes users and the protocol to race conditions where attackers can exploit existing allowances during the approval process, especially for tokens requiring zero-reset on updates (e.g., USDT). This oversight can lead to unauthorized token transfers and financial losses.


Detailed Analysis

Root Cause

The vulnerability arises from how _approveCollateralTokenForAave updates token allowances using safeIncreaseAllowance without first resetting the allowance to zero. This approach does not comply with ERC20 best practices and creates opportunities for front-running attacks.

Relevant Code:

function _approveCollateralTokenForAave(address _collateralToken) internal {
uint256 currentAllowance = IERC20Metadata(_collateralToken).allowance(address(this), _aaveV3Pool);
IERC20Metadata(_collateralToken).safeIncreaseAllowance(_aaveV3Pool, type(uint256).max - currentAllowance);
}

This implementation allows attackers to utilize the current allowance during the pending transaction to exploit the newly set allowance.


Attack Scenario

**Obj->**Exploit the race condition in the allowance update process to execute unauthorized token transfers.

Step-by-Step

  1. Approval Attempt:

    • The protocol owner calls _approveCollateralTokenForAave to update the token allowance for the Aave V3 Pool.

  2. Front-Running Opportunity:

    • An attacker monitors the blockchain for pending transactions that invoke _approveCollateralTokenForAave.

  3. Exploitation:

    • Before the approval transaction is mined, the attacker submits a transaction to spend the current allowance.

  4. Post-Approval Exploit:

    • Once the original transaction is mined, the allowance is updated to a new maximum value, enabling the attacker to spend both the remaining allowance and the newly approved allowance.


Impact

Consequences:

  1. Unauthorized Token Transfers:

    • Attackers can drain tokens from the protocol or user wallets by exploiting the allowance race condition.

  2. Financial Losses:

    • Users and the protocol may incur significant financial losses due to unauthorized token expenditures.

  3. Eroded Trust:

    • Security flaws in the allowance mechanism can undermine user confidence in the protocol's reliability.


Proof of Concept (PoC)

The following contract demonstrates how an attacker can exploit the front-running vulnerability:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract ApprovalAttacker {
IERC20 public token;
address public target;
address public attacker;
constructor(address _token, address _target) {
token = IERC20(_token);
target = _target;
attacker = msg.sender;
}
// Exploit function to front-run approvals
function exploit(uint256 amount) external {
require(msg.sender == attacker, "Unauthorized");
// Use the current allowance before it is updated
token.transferFrom(target, attacker, amount);
}
}

Proposed Solution

1. Safe Approval Patterns

Modify _approveCollateralTokenForAave to reset the allowance to zero before updating it:

function _approveCollateralTokenForAave(address _collateralToken) internal {
require(_collateralTokenToWToken[_collateralToken] != address(0), "Collateral token not registered");
// Reset allowance to zero before setting it to maximum
IERC20Metadata(_collateralToken).safeApprove(_aaveV3Pool, 0);
IERC20Metadata(_collateralToken).safeApprove(_aaveV3Pool, type(uint256).max);
}

Benefit:

Prevents exploitation in tokens requiring zero-reset on allowance updates (e.g., USDT).

2. Utilize Permit Mechanism (EIP-2612)

Adopt the permit function to enable off-chain approval through signatures, eliminating on-chain approval transactions.

interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
function _approveCollateralTokenForAaveWithPermit(
address _collateralToken,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
IERC20Permit(_collateralToken).permit(msg.sender, _aaveV3Pool, value, deadline, v, r, s);
}

Benefit:

Reduces the risk of front-running by eliminating on-chain approval transactions.

3. Restrict Token Standards

Ensure that only tokens adhering to strict ERC20 behavior are allowed:

function _isERC20(address _token) internal view returns (bool) {
try IERC20Metadata(_token).totalSupply() returns (uint256) {
return true;
} catch {
return false;
}
}
function _registerCollateralToken(address _collateralToken) internal {
require(_isERC20(_collateralToken), "Only ERC20 tokens are supported");
}
Updates

Lead Judging Commences

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

Support

FAQs

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