Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: low
Valid

Griefing attack via `LendingPool::updateState()` function.

Summary

Contract - LendingPool.sol.

The updateState() function is -

function updateState() external {
ReserveLibrary.updateReserveState(reserve, rateData);
}

ReserveLibrary.updateReserveState() ->

function updateReserveState(ReserveData storage reserve,ReserveRateData storage rateData) internal {
updateReserveInterests(reserve, rateData);
}

ReserveLibrary.updateReserveInterests() ->

function updateReserveInterests(ReserveData storage reserve,ReserveRateData storage rateData) internal {
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
@-> return;
}
uint256 oldLiquidityIndex = reserve.liquidityIndex;
if (oldLiquidityIndex < 1) revert LiquidityIndexIsZero();
// Update liquidity index using linear interest
reserve.liquidityIndex = calculateLiquidityIndex(
rateData.currentLiquidityRate,
timeDelta,
reserve.liquidityIndex
);
// Update usage index (debt index) using compounded interest
reserve.usageIndex = calculateUsageIndex(
rateData.currentUsageRate,
timeDelta,
reserve.usageIndex
);
// Update the last update timestamp
@-> reserve.lastUpdateTimestamp = uint40(block.timestamp);
emit ReserveInterestsUpdated(reserve.liquidityIndex, reserve.usageIndex);
}

The problem is that updateReserveInterests is also being called inside deposit(), withdraw() and updateInterestRatesAndLiquidity() functions of LendingPool.sol.

Vulnerability Details

  1. An attacker sees transactions of deposit(), withdraw() or updateInterestRatesAndLiquidity() in mempool.

  2. He will frontrun those txs, by paying extra gas fee calling updateState() -

function updateState() external {
ReserveLibrary.updateReserveState(reserve, rateData);
}
  1. This will update set lastUpdateTimestamp to current block.timestamp reserve.lastUpdateTimestamp = uint40(block.timestamp); .

  2. Now when the any of transaction deposit(), withdraw() or updateInterestRatesAndLiquidity() will be executed, they will return without performing any operation because timeDelta will be 0 -

uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return;
}
  1. Attacker can perform this attack operation everytime, causing unintended behaviors.

Impact

  • Greifing attack for functions deposit(), withdraw() or updateInterestRatesAndLiquidity().

  • Leading to unexpected behavior, and also potentially can cause loss of funds to both protocol and user.

Tools Used

Manual

Recommendations

Put access modifier on updateState() function, for admin or owner.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::updateState() lacks access control, allowing attackers to front-run transactions and disrupt interest accrual by repeatedly updating lastUpdateTimestamp

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::updateState() lacks access control, allowing attackers to front-run transactions and disrupt interest accrual by repeatedly updating lastUpdateTimestamp

Support

FAQs

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

Give us feedback!