Summary
User are allowed to depositNFT tokens as collaterals for their loan but in an extreme case when a users collateral price drops and he is slightly below the 1e18 health factor, the user can try to deposit a new NFT to increase his collateral ratio but an attacker can front run this call and make users account marked for liquidation and this cannot be unremoved unless the user repays this loan or face liquidation.
Vulnerability Details
Attacker can frontrun user's deposit to initiate liquidation
* @notice Allows anyone to initiate the liquidation process if a user's health factor is below threshold
* @param userAddress The address of the user to liquidate
*/
function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
ReserveLibrary.updateReserveState(reserve, rateData);
UserData storage user = userData[userAddress];
uint256 healthFactor = calculateHealthFactor(userAddress);
@audit>> if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
@audit>> isUnderLiquidation[userAddress] = true;
@audit>> liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
}
Even though the user is able to deposit and get his health factor above the liquidation point the position still remain marked as liquidatablea and only debt repayment alone can reverse this action. Forcing the user to repay to avoid losing his collateral.
* @notice Allows a user to repay their debt and close the liquidation within the grace period
*/
function closeLiquidation() external nonReentrant whenNotPaused {
address userAddress = msg.sender;
@audit>> if (!isUnderLiquidation[userAddress]) revert NotUnderLiquidation();
ReserveLibrary.updateReserveState(reserve, rateData);
if (block.timestamp > liquidationStartTime[userAddress] + liquidationGracePeriod) {
revert GracePeriodExpired();
}
UserData storage user = userData[userAddress];
uint256 userDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex);
@audit>> if (userDebt > DUST_THRESHOLD) revert DebtNotZero();
isUnderLiquidation[userAddress] = false;
liquidationStartTime[userAddress] = 0;
emit LiquidationClosed(userAddress);
}
Without repaying the debt to a value below DUST user will face liquidation .
Impact
Attacker can force a user to repay his loan by frontrunning his call to deposit funds
Tools Used
Manual Review
Recommendations
When a user deposits if they have a position check to ensure the health factor is now above 1e18 and set the liquidation mapping to set and time to 0.