Core Contracts

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

User can borrow more than their collateral leading to insolvent positions and undercollateralization

Summary

The LendingPool contract contains a vulnerability in its borrow function that allows users with RAACNft to borrow more than their collateral value. This issue arises from an improper collateral check, potentially leading to insolvent positions and bad debt for the protocol.

Vulnerability Details

LendingPool::borrow allows users with RAACNft to borrow assets from the protocol. See function below:

/**
* @notice Allows a user to borrow reserve assets using their NFT collateral
* @param amount The amount of reserve assets to borrow
*/
function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
if (isUnderLiquidation[msg.sender]) revert CannotBorrowUnderLiquidation();
UserData storage user = userData[msg.sender];
uint256 collateralValue = getUserCollateralValue(msg.sender);
if (collateralValue == 0) revert NoCollateral();
// Update reserve state before borrowing
ReserveLibrary.updateReserveState(reserve, rateData);
// Ensure sufficient liquidity is available
_ensureLiquidity(amount);
// Fetch user's total debt after borrowing
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
// Ensure the user has enough collateral to cover the new debt
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
// Update user's scaled debt balance
uint256 scaledAmount = amount.rayDiv(reserve.usageIndex);
// Mint DebtTokens to the user (scaled amount)
(bool isFirstMint, uint256 amountMinted, uint256 newTotalSupply) = IDebtToken(reserve.reserveDebtTokenAddress).mint(msg.sender, msg.sender, amount, reserve.usageIndex);
// Transfer borrowed amount to user
IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount);
user.scaledDebtBalance += scaledAmount;
// reserve.totalUsage += amount;
reserve.totalUsage = newTotalSupply;
// Update liquidity and interest rates
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, 0, amount);
// Rebalance liquidity after borrowing
_rebalanceLiquidity();
emit Borrow(msg.sender, amount);
}

The vulnerability lies in the following line:

if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}

The line checks if the user's collateral value is less than a percentage of their debt. This means that if the collateral value is higher than whatever that percentage is, it means that the user can borrow more than their collateral which removes the incentive to repay debt and leaves the protocol with bad debt via insolvent positions.

Proof Of Code (POC)

This test was run in the LendingPool.test.js file in the "Borrow and repay" describe block

it("user can borrow more than their collateral", async function () {
//c for testing purposes
const userCollateral = await lendingPool.getUserCollateralValue(
user1.address
);
console.log("userCollateral", userCollateral);
const borrowAmount = userCollateral + ethers.parseEther("20");
console.log("borrowAmount", borrowAmount);
await lendingPool.connect(user1).borrow(borrowAmount);
assert(borrowAmount > userCollateral);
});

Impact

Users can borrow more than their collateral, leading to insolvency risks.

The protocol could accumulate bad debt due to users defaulting on excessive loans.

Attackers could exploit this issue to drain liquidity from the protocol, harming lenders and destabilizing the system.

Tools Used

Manual Review, Hardhat

Recommendations

Use a More Accurate Collateralization Check: Modify the borrowing condition to ensure that a user’s collateral value must always exceed their debt based on a stricter collateralization ratio.

Example fix:

if (collateralValue * COLLATERAL_RATIO < userTotalDebt) {
revert NotEnoughCollateralToBorrow();
}

Introduce Safe Borrow Limits: Implement a maximum Loan-To-Value (LTV) ratio to prevent excessive borrowing.

Updates

Lead Judging Commences

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

LendingPool::borrow as well as withdrawNFT() reverses collateralization check, comparing collateral < debt*0.8 instead of collateral*0.8 > debt, allowing 125% borrowing vs intended 80%

Support

FAQs

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