Denial-of-Service Attack caused by underflow

From WEB3 Vulnerapedia
Jump to navigation Jump to search

Denial-of-service (DoS) attack caused by underflow typically involves exploiting a vulnerability in a system where a numerical calculation results in a value less than the minimum representable value for the data type, causing an underflow. This could lead to various consequences such as crashes, freezes, or unresponsiveness of the targeted system, rendering it inaccessible to legitimate users.

Underflow occurs when a calculation results in a value that is smaller than the minimum representable value for the data type being used. In programming, particularly in languages like C or C++, where memory management is more explicit, underflows can occur due to improper handling of numeric values, inadequate input validation, or unchecked arithmetic operations.

harvestFees() function from contract DnGmxJuniorVaultManager.sol can push junior vault borrowedUSDC above borrow cap and DOS vault

DnGmxJuniorVaultManager#harvestFees grants fees to the senior vault by converting the WETH to USDC and staking it directly.

The result is that the senior vault gains value indirectly by increasing the debt of the junior vault. If the junior vault is already at its borrow cap this will push its total borrow over the borrow cap causing DnGmxSeniorVault#availableBorrow to underflow and revert.

This is called each time a user deposits or withdraws from the junior vault, meaning that the junior vault can no longer deposit or withdraw.

if (_seniorVaultWethRewards > state.wethConversionThreshold) {
    // converts senior tranche share of weth into usdc and deposit into AAVE
    // Deposit aave vault share to AAVE in usdc
    uint256 minUsdcAmount = _getTokenPriceInUsdc(state, state.weth).mulDivDown(
        _seniorVaultWethRewards * (MAX_BPS - state.slippageThresholdSwapEthBps),
        MAX_BPS * PRICE_PRECISION
    );
    // swaps weth into usdc
    (uint256 aaveUsdcAmount, ) = state._swapToken(
        address(state.weth),
        _seniorVaultWethRewards,
        minUsdcAmount
    );

    // supplies usdc into AAVE
    state._executeSupply(address(state.usdc), aaveUsdcAmount);

    // resets senior tranche rewards
    state.seniorVaultWethRewards = 0;

The above lines convert the WETH owed to the senior vault to USDC and deposit it into Aave. Increasing the aUSDC balance of the junior vault.

function getUsdcBorrowed() public view returns (uint256 usdcAmount) {
  return
      uint256(
          state.aUsdc.balanceOf(address(this)).toInt256() -
              state.dnUsdcDeposited -
              state.unhedgedGlpInUsdc.toInt256()
      );
}

The amount of USDC borrowed is calculated based on the amount of aUSDC that the junior vault has. By depositing the fees directly above, the junior vault has effectively “borrowed” more USDC. This can be problematic if the junior vault is already at its borrowing cap.

function availableBorrow(address borrower) public view returns (uint256 availableAUsdc) {
    uint256 availableBasisCap = 
      borrowCaps[borrower] - IBorrower(borrower).getUsdcBorrowed();
    uint256 availableBasisBalance = aUsdc.balanceOf(address(this));

    availableAUsdc = availableBasisCap < availableBasisBalance ? 
        availableBasisCap : availableBasisBalance;
}

If the vault is already at its borrowing cap then the line calculating availableBasisCap will underflow and revert.

Recommendation

Check if borrowed exceeds borrow cap and return zero to avoid underflow:

function availableBorrow(address borrower) public view returns (uint256 availableAUsdc) {

+   uint256 borrowCap = borrowCaps[borrower];
+   uint256 borrowed = IBorrower(borrower).getUsdcBorrowed();

+   if (borrowed > borrowCap) return 0;

+   uint256 availableBasisCap = borrowCap - borrowed;

-   uint256 availableBasisCap = borrowCaps[borrower] - IBorrower(borrower).getUsdcBorrowed();
    uint256 availableBasisBalance = aUsdc.balanceOf(address(this));

    availableAUsdc = availableBasisCap < availableBasisBalance ? availableBasisCap : availableBasisBalance;
}

Sources

https://medium.com/@bloqarl/uncovering-real-life-examples-of-denial-of-service-attacks-on-smart-contracts-8bc220c2cdd0