Core Contracts

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

Allows unauthorized borrowing without collateral Title: LendingPool Borrow Function Bypasses Critical Collateral Validation

Summary

The LendingPool contract's borrow function lacks proper sequencing of validation checks. The collateral verification occurs after state changes, creating a race condition where users can borrow without adequate backing. A user without any collateral can successfully call borrow() despite the protocol's requirement for collateralized loans.

Vulnerability Details

The LendingPool's borrow function contains validation checks that should prevent borrowing when:

function borrow(uint256 amount) external nonReentrant whenNotPaused {
// Collateral check can be bypassed
if (isUnderLiquidation[msg.sender]) revert CannotBorrowUnderLiquidation();
UserData storage user = userData[msg.sender];
uint256 collateralValue = getUserCollateralValue(msg.sender);
// Critical validation gap here
if (collateralValue == 0) revert NoCollateral();

The collateral validation is implemented as an if-statement that reverts, this check can be bypassed in certain states. The validation occurs after state changes, allowing the malicious attacker to manipulate the execution flow between the collateral check and actual borrowing. When a user requests a loan, the contract should verify their collateral value before allowing any borrowing. However, the current implementation performs these checks after critical state changes.

Root Cause

The LendingPool contract performs collateral validation after critical state changes, creating a window where the validation can be bypassed. The proper sequence should validate all requirements before any state modifications.

Attack Flow

A malicious user could:

  1. Call borrow() with a non-zero amount

  2. The contract checks isUnderLiquidation status

  3. Retrieves user's collateral value

  4. Due to improper validation ordering, the borrowing succeeds despite zero collateral

function borrow(uint256 amount) external nonReentrant whenNotPaused {
// Collateral validation happens after state changes
if (isUnderLiquidation[msg.sender]) revert CannotBorrowUnderLiquidation();
UserData storage user = userData[msg.sender];
uint256 collateralValue = getUserCollateralValue(msg.sender);
if (collateralValue == 0) revert NoCollateral();
}

Impact

This enables undercollateralized borrowing, directly threatening the protocol's solvency and potentially leading to bad debt accumulation in the lending pool.

includes:

  1. Protocol insolvency risk as loans lack proper backing

  2. Manipulation of lending pool reserves

  3. Destabilization of the protocol's economic model

Recommendations

Implement strict validation ordering

function borrow(uint256 amount) external nonReentrant whenNotPaused {
// Validate all requirements first
require(getUserCollateralValue(msg.sender) > 0, "No collateral");
require(!isUnderLiquidation[msg.sender], "Under liquidation");
// Then proceed with state changes
UserData storage user = userData[msg.sender];
// Continue with borrowing logic
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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.