Core Contracts

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

Stale Price Oracle Data

Summary

No check for stale oracle prices, risking undercollateralized loans.

/**
* Checks if the price is stale
*/
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price;

Vulnerability Details

LendingPool's getNFTPrice() retrieves oracle prices without validating staleness or deviation limits, enabling loans against potentially outdated or manipulated NFT valuations.

No staleness check despite comment indicating it should check

/**
* @notice Gets the current price of an NFT from the oracle
* @param tokenId The token ID of the NFT
* @return The price of the NFT
*
* Checks if the price is stale
*/

In a State where

  • NFT price = 100 ETH at timestamp T

  • Price oracle stops updating

  • Market price drops to 10 ETH

Exploitation

// vulnerable code
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price; // Returns potentially stale or manipulated price
}
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice(); // Only checks non-zero
return price; // Returns stale 100 ETH price
}
// Attacker borrows against overvalued NFT
function borrow(uint256 amount) external {
uint256 collateralValue = getNFTPrice(tokenId); // Uses stale price
// Borrows 80 ETH against NFT worth only 10 ETH
}

Will result in:

  • Loans issued at 100 ETH valuation

  • Actual collateral value = 10 ETH

  • Protocol becomes undercollateralized

  • Liquidations fail to cover debt

Mango Markets oracle manipulation attack ($114M loss) demonstrates how insufficient price validation leads to undercollateralized positions. Both cases show how DeFi lending protocols requiring accurate collateral valuation can be exploited through oracle trust assumptions.

Impact

function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price; // Returns potentially stale or manipulated price
}
  • Loans issued at 10x actual collateral value

  • Liquidations fail to cover outstanding debt

  • Maximum loss = Sum of all loans issued against stale/manipulated prices

Tools Used

vs

Recommendations

Add a time threshold for price validity

// Add constants
uint256 private constant PRICE_STALENESS_THRESHOLD = 24 hours;
uint256 private constant MIN_NFT_PRICE = 1000; // Minimum acceptable price
uint256 private constant MAX_PRICE_DEVIATION = 50; // 50% max deviation
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
// Validate price freshness
if (block.timestamp - lastUpdateTimestamp > PRICE_STALENESS_THRESHOLD) {
revert StalePriceData();
}
// Validate price bounds
if (price == 0 || price < MIN_NFT_PRICE) revert InvalidNFTPrice();
return price;
}
Updates

Lead Judging Commences

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

LendingPool::getNFTPrice or getPrimeRate doesn't validate timestamp staleness despite claiming to, allowing users to exploit outdated collateral values during price drops

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

LendingPool::getNFTPrice or getPrimeRate doesn't validate timestamp staleness despite claiming to, allowing users to exploit outdated collateral values during price drops

Support

FAQs

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