getNFTPrice() does not check for price staleness even though it claims so:
The flow of fetching price is:
Chainlink Oracle -> RAACHousePriceOracle
RAACHousePriceOracle -> RAACHousePrices
RAACHousePrices -> LendingPool::getNFTPrice()
Since getNFTPrice()
is internally called by critical functions withdrawNFT()
and getUserCollateralValue()
(which is internally called by borrow()
etc.) an outdated price can result in the user borrowing more than allowed, thus profiting and putting the protocol underwater.
Attack Path:
Monitor old price: 100 ETH --> Wait for market drop --> Real value now 60 ETH (suppose that price drop happened over 24 hours)
Buy NFT for 60 ETH from the open market
Deposit NFT in LendingPool
Borrow 80 ETH against NFT, since price reported by getNFTPrice()
is still 100 ETH
Default on loan
Keep 20 ETH profit
Add a check which verifies the age. Something along these lines:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.