When a vault is to be liquidated by SmartVaultManagerV5.sol#liquidateVault() the return value of SmartVaultV3.sol#undercollateralised() is checked against and if true, the vault is liquidated.
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/SmartVaultManagerV5.sol#L81-L92
The way undercollateralised() calculates whether to return true/false is by checking if minted > maxMintable():
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/SmartVaultV3.sol#L99-L101
Where maxMintable() takes a value from euroCollateral()
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/SmartVaultV3.sol#L75-L77
euroCollateral() loops through the accepted tokens list and invokes calculator.tokenToEurAvg().
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/SmartVaultV3.sol#L67-L73
If we take a closer look tokenToEurAvg() we can see that it returns data based of a Chainlink price feed.
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/utils/PriceCalculator.sol#L43-L49
The issue is that the return values taken from latestRoundData() have no sanity checks whatsoever, price can be stale and inaccurate which means that tokenToEurAvg() could produce wrong results. If that happens, maxMintable() can produce wrong results and pass the check for undercollaterised() where minted > maxMintable(). Although this might be a rare instance, this would allow a vault that contains exactly 1 collateral token which returns a stale result, to be liquidated when it shouldn't be.
Another thing I want to note is that anyone can call LiquidationPoolManager.sol#runLiquidation() for a vaultId represented as tokenId. There is no access control whatsoever. This allows anyone to attempt to liquidate a vault. The runLiquidation() function invokes SmartVaultMangerV5.sol#liquidateVault() with permissions.
https://github.com/Cyfrin/2023-12-the-standard/blob/91132936cb09ef9bf82f38ab1106346e2ad60f91/contracts/LiquidationPoolManager.sol#L59-L82
Although the contract PriceCalculator.sol which returns the value for tokenToEurAvg() is out of scope, the issue itself occurs because of functions invoked in the contracts in scope. I believe the issue is dangerous and upon consulting with the developer of the protocol in a private thread, they advised me to submit this due to its urgency and leave it up to the judge to decide. I have decided to submit it as Medium since it is rather unlikely but the impact is high.
Vault can be liquidate when it shouldnt be
Manual Review
Include sanity checks for getLatestData()
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.