Skip to content

Latest commit

 

History

History
87 lines (64 loc) · 4.86 KB

084.md

File metadata and controls

87 lines (64 loc) · 4.86 KB

Obedient Lava Monkey

Medium

Health Factor Validation Fails to Consider Post-Borrow State, Allowing Immediate Liquidation Risks

Summary

The calculation of healthFactor before accounting for the new borrow amount results in an inaccurate validation of the health factor for borrowers. This allows users to borrow amounts that lower their health factor below the liquidation threshold, leading to immediate liquidation risks upon borrowing.


Root Cause

The issue occurs in the ValidationLogic.validateBorrow function. Specifically, the healthFactor is validated before the new borrow amount is added to the user's debt. This is demonstrated by the following validation logic:

require(
  vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
  Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
);

This check uses outdated values of userDebtInBaseCurrency and healthFactor that do not account for the impact of the new loan. As a result, borrowers can take out loans that drop their actual health factor below the liquidation threshold, bypassing proper solvency checks.

The issue lies in these two steps of the borrow validation process:

  1. The current health factor is checked using the pre-borrow state:
    require(
      vars.healthFactor > HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
      Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
    );
  2. The collateral coverage is separately validated based on the loan-to-value (LTV) requirement:
    vars.collateralNeededInBaseCurrency = (vars.userDebtInBaseCurrency + vars.amountInBaseCurrency)
      .percentDiv(vars.currentLtv);
    
    require(
      vars.collateralNeededInBaseCurrency <= vars.userCollateralInBaseCurrency,
      Errors.COLLATERAL_CANNOT_COVER_NEW_BORROW
    );

Key Observations:

  • The healthFactor validation occurs using the pre-borrow state.
  • The LTV validation considers the new borrow amount but does not recalculate the health factor after adding the new debt.
  • These two checks are fundamentally different: satisfying the LTV requirement does not guarantee that the borrower's health factor remains above the liquidation threshold.

As a result, the following attack scenario is possible:

  • A borrower with a health factor slightly above 1 requests a loan that satisfies the LTV requirement.
  • The protocol approves the borrow based on the outdated health factor.
  • After the loan is executed, the borrower’s health factor drops below 1, making them immediately liquidatable.

To address this issue, the health factor must be recalculated after incorporating the new borrow amount and validated before the borrow is finalized.


Internal Pre-conditions

  1. The borrower has collateral supplied, resulting in a calculated healthFactor just above the liquidation threshold.
  2. The borrower requests a loan amount that would drop their post-borrow health factor below the threshold.
  3. The validateBorrow function does not revalidate the updated health factor after including the new borrow amount.

External Pre-conditions

  1. The oracle provides asset price information for the borrower’s collateral.
  2. The borrowed asset has sufficient liquidity in the pool.

Attack Path

  1. A borrower supplies collateral with a health factor near the liquidation threshold (e.g., 1.01).
  2. The borrower calls the borrow function, requesting an amount that lowers their health factor to below 1 after accounting for the new debt.
  3. The protocol approves the borrow request since the health factor check uses pre-borrow values and does not consider the impact of the new loan.
  4. The borrower becomes immediately liquidatable, leading to liquidation risks and potential collateral loss.

Impact

  • Borrowers are exposed to immediate liquidation risks, potentially losing a significant portion of their collateral.
  • The protocol incurs unnecessary liquidation activity, which disrupts overall stability and erodes user trust.

Mitigation

The ValidationLogic.validateBorrow function should recalculate the health factor after adding the new borrow amount to the user's debt. This recalculated health factor must then be validated to ensure that it remains above the liquidation threshold before finalizing the borrow request.