Skip to content

Latest commit

 

History

History
99 lines (80 loc) · 4.99 KB

033.md

File metadata and controls

99 lines (80 loc) · 4.99 KB

Obedient Lava Monkey

Medium

reserve.updateInterestRatesAndVirtualBalance Updates Virtual Balances Without Validation

Summary

Unvalidated input added will cause reserve data corruption for protocol users as an attacker will pass an invalid value for added to manipulate the reserve state.


Root Cause

In BridgeLogic.sol, the function updateInterestRatesAndVirtualBalance from ReserveLogic is called with added without prior validation. The logic inside updateInterestRatesAndVirtualBalance updates the reserve's state based on these inputs.

 function updateInterestRatesAndVirtualBalance(
    DataTypes.ReserveData storage reserve,
    DataTypes.ReserveCache memory reserveCache,
    address reserveAddress,
    uint256 liquidityAdded,
    uint256 liquidityTaken
  ) internal {
    uint256 totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul(
      reserveCache.nextVariableBorrowIndex
    );

    (uint256 nextLiquidityRate, uint256 nextVariableRate) = IReserveInterestRateStrategy(
      reserve.interestRateStrategyAddress
    ).calculateInterestRates(
        DataTypes.CalculateInterestRatesParams({
          unbacked: reserve.unbacked + reserve.deficit,
          liquidityAdded: liquidityAdded,
          liquidityTaken: liquidityTaken,
          totalDebt: totalVariableDebt,
          reserveFactor: reserveCache.reserveFactor,
          reserve: reserveAddress,
          usingVirtualBalance: reserveCache.reserveConfiguration.getIsVirtualAccActive(),
          virtualUnderlyingBalance: reserve.virtualUnderlyingBalance
        })
      );

    reserve.currentLiquidityRate = nextLiquidityRate.toUint128();
    reserve.currentVariableBorrowRate = nextVariableRate.toUint128();

    // Only affect virtual balance if the reserve uses it
    if (reserveCache.reserveConfiguration.getIsVirtualAccActive()) {
      if (liquidityAdded > 0) {
        reserve.virtualUnderlyingBalance += liquidityAdded.toUint128();
      }
      if (liquidityTaken > 0) {
        reserve.virtualUnderlyingBalance -= liquidityTaken.toUint128();
      }
    }

    emit ReserveDataUpdated(
      reserveAddress,
      nextLiquidityRate,
      0,
      nextVariableRate,
      reserveCache.nextLiquidityIndex,
      reserveCache.nextVariableBorrowIndex
    );
  }
  • The function updateInterestRatesAndVirtualBalance unconditionally updates the reserve's liquidity and borrow indices, as well as the virtual underlying balance, based on the inputs liquidityAdded and liquidityTaken.
  • Neither liquidityAdded nor liquidityTaken is validated before these updates. If either of these inputs is invalid (e.g., liquidityAdded being excessively large or negative due to underflow), it directly affects the reserve’s state.
  • In BridgeLogic, the value of added passed to this function is computed as backingAmount + fee, which is similarly not validated, allowing invalid values to propagate and corrupt the state. This lack of validation makes the reserve susceptible to data corruption, directly affecting users' interest rates and the protocol's stability.

Internal Pre-conditions

  1. The backingAmount and fee are set to values that cause added to exceed protocol-defined limits or be zero.
  2. executeBackUnbacked is called, passing the invalid added to updateInterestRatesAndVirtualBalance.

External Pre-conditions

  1. The attacker or malicious actor has access to call executeBackUnbacked with crafted inputs.
  2. The protocol does not have upper or lower bounds set for added in its calculations.

Attack Path

  1. The attacker calls executeBackUnbacked with an excessively high fee or backingAmount.
  2. added becomes an invalid value (e.g., too large or zero).
  3. updateInterestRatesAndVirtualBalance updates the reserve state with this invalid value.
  4. The reserve’s liquidity and borrow indices are corrupted, affecting interest rate calculations and user balances.

Impact

The protocol’s users suffer an approximate loss due to incorrect reserve state updates, leading to miscalculations in user balances and interest rates. The attacker might gain an advantage by manipulating the protocol’s interest rates or causing reserve instability.


Mitigation

Validate added before calling updateInterestRatesAndVirtualBalance to ensure it is within acceptable bounds:

require(added > 0 && added <= MAX_RESERVE_AMOUNT, Errors.INVALID_BALANCE_UPDATE);