Core Contracts

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

Function `liquidateBorrower()` in StabilityPool wrongly computes `scaledUserDebt`

Summary

In liquidateBorrower() , the call to getUserDebt(), returns *scaled * userDebt.

However, the function incorrectly scales it again, which doubles the userDebt, and eventually gives a wrong approval of token to the lendingPool

Vulnerability Details

In order to liquidate borrowers in lendingPool, we need to call liquidateBorrower() in StabilityPool contract.

To cover the debt, the stability pool gives an approval to lendingPool for transferring an equal amount of userDebt to the rToken contract in crvUSD token.
However, the approval given to lendingPool is WRONG.
Function liquidateBorrower()

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
// Get the user's debt from the LendingPool.
uint256 userDebt = lendingPool.getUserDebt(userAddress);
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
if (userDebt == 0) revert InvalidAmount();
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
// Approve the LendingPool to transfer the debt amount
bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);

Notice the call to getUserDebt(), we are incorrectly assuming that the debt returned has not been SCALED, when infact, it is scaled User Debt that is returned.

So, the computation of scaledUserDebt almost doubles the amount, hence, a wrong approval is given to the lendingPool

This results in a much larger approval than normally intended, which goes beyond the intended behaviour.

Impact

Large approval for crvUSDToken will be given to the lendingPool

Tools Used

Manual

Recommendations

Consider using the userDebt directly because it has already been SCALED:

uint256 userDebt = lendingPool.getUserDebt(userAddress);
- uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
if (userDebt == 0) revert InvalidAmount();
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
- if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
+ if (crvUSDBalance < userDebt) revert InsufficientBalance();
+ bool approveSuccess = crvUSDToken.approve(address(lendingPool), userDebt); // use userDebt here
```
Updates

Lead Judging Commences

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

StabilityPool::liquidateBorrower double-scales debt by multiplying already-scaled userDebt with usage index again, causing liquidations to fail

Support

FAQs

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