Skip to content

Latest commit

 

History

History
135 lines (96 loc) · 4.75 KB

094.md

File metadata and controls

135 lines (96 loc) · 4.75 KB

Tiny Licorice Loris

Medium

A Paused reserve can still be used as collateral and can be supplied to

Summary

BridgeLogic.executeMintUnbacked() fails to check if the reserve to be minted is paused.

https://github.com/sherlock-audit/2025-01-aave-v3-3/blob/main/aave-v3-origin/src/contracts/protocol/libraries/logic/BridgeLogic.sol#L57

Root Cause

 function executeMintUnbacked(//@audit-issue A frozen, A paused reserve can still be supplied to and used as collateral.
    mapping(address => DataTypes.ReserveData) storage reservesData,
    mapping(uint256 => address) storage reservesList,
    DataTypes.UserConfigurationMap storage userConfig,
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
  ) external {
    DataTypes.ReserveData storage reserve = reservesData[asset];
    DataTypes.ReserveCache memory reserveCache = reserve.cache();

    reserve.updateState(reserveCache);

    ValidationLogic.validateSupply(reserveCache, reserve, amount, onBehalfOf);

    uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap();
    uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals();

    uint256 unbacked = reserve.unbacked += amount.toUint128();

    require(
      unbacked <= unbackedMintCap * (10 ** reserveDecimals),
      Errors.UNBACKED_MINT_CAP_EXCEEDED
    );

    reserve.updateInterestRatesAndVirtualBalance(reserveCache, asset, 0, 0);

    bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(//@audit-ok `executeMintUnbacked()` can fail to `setUsingAsCollateral()` for first-time supply user. (A malicious user can prevent this by transferring a very minuscule amount to `onBehalfOf`)
      msg.sender,
      onBehalfOf,
      amount,
      reserveCache.nextLiquidityIndex
    );

    if (isFirstSupply) {
      if (
        ValidationLogic.validateAutomaticUseAsCollateral(
          reservesData,
          reservesList,
          userConfig,
          reserveCache.reserveConfiguration,
          reserveCache.aTokenAddress
        )
      ) {
        userConfig.setUsingAsCollateral(reserve.id, true);
        emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
      }
    }

    emit MintUnbacked(asset, msg.sender, onBehalfOf, amount, referralCode);
  }

There's no check whatsoever in the logic of BridgeLogic.executeMintUnbacked() to ascertain if the reserve to be minted is paused by admins.

So users can still mint unbacked for paused reserves and supply to them later via BridgeLogic.executeBackUnbacked()

This breaks the invariant where paused reserves shouldn't allow new supplies

 /**
   * @notice Pauses a reserve. A paused reserve does not allow any interaction (supply, borrow, repay,
   * swap interest rate, liquidate, atoken transfers).
   * @param asset The address of the underlying asset of the reserve
   * @param paused True if pausing the reserve, false if unpausing
   * @param gracePeriod Count of seconds after unpause during which liquidations will not be available
   *   - Only applicable whenever unpausing (`paused` as false)
   *   - Passing 0 means no grace period
   *   - Capped to maximum MAX_GRACE_PERIOD
   */
  function setReservePause(address asset, bool paused, uint40 gracePeriod) external;

Internal Pre-conditions

The logic of BridgeLogic.executeMintUnbacked() fails to check if the asset reserve to be minted is paused.

External Pre-conditions

Can happen anytime, all the time

Attack Path

  1. Admins pause an asset reserve via Poolconfigurator.setReserveFreeze()
  2. User doesn't know, he uses BridgeLogic.executeMintUnbacked() for that asset reserve and if all conditions are met it even sets the asset reserve as collateral for him
  3. User later supplies to the reserve later via BridgeLogic.executeBackUnbacked()

Impact

A asset reserve paused by admins can still be used as collateral and supplied to by users.

This breaks the invariant where paused reserves shouldn't allow new supplies

 /**
   * @notice Pauses a reserve. A paused reserve does not allow any interaction (supply, borrow, repay,
   * swap interest rate, liquidate, atoken transfers).
   * @param asset The address of the underlying asset of the reserve
   * @param paused True if pausing the reserve, false if unpausing
   * @param gracePeriod Count of seconds after unpause during which liquidations will not be available
   *   - Only applicable whenever unpausing (`paused` as false)
   *   - Passing 0 means no grace period
   *   - Capped to maximum MAX_GRACE_PERIOD
   */
  function setReservePause(address asset, bool paused, uint40 gracePeriod) external;

PoC

No response

Mitigation

check the flag of the asset reserve that BridgeLogic.executeMintUnbacked() is to be used for and revert if it is paused