Obedient Lava Monkey
Medium
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.
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 inputsliquidityAdded
andliquidityTaken
. - Neither
liquidityAdded
norliquidityTaken
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 ofadded
passed to this function is computed asbackingAmount + 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.
- The backingAmount and fee are set to values that cause
added
to exceed protocol-defined limits or be zero. executeBackUnbacked
is called, passing the invalidadded
toupdateInterestRatesAndVirtualBalance
.
- The attacker or malicious actor has access to call
executeBackUnbacked
with crafted inputs. - The protocol does not have upper or lower bounds set for
added
in its calculations.
- The attacker calls
executeBackUnbacked
with an excessively highfee
orbackingAmount
. added
becomes an invalid value (e.g., too large or zero).updateInterestRatesAndVirtualBalance
updates the reserve state with this invalid value.- The reserve’s liquidity and borrow indices are corrupted, affecting interest rate calculations and user balances.
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.
Validate added
before calling updateInterestRatesAndVirtualBalance
to ensure it is within acceptable bounds:
require(added > 0 && added <= MAX_RESERVE_AMOUNT, Errors.INVALID_BALANCE_UPDATE);