codehawks-2023-07-dsc-m09

[M-09] liquidate does not allow the liquidator to liquidate a user if the liquidator HF < 1

보고서

Summary

불필요하게 체크하여 담보가 부족한 유저는 liquidate를 호출하여 다른 유저를 청산시킬 수 없다.

Keyword

business logic vul

Vulnerability

청산자의 담보가 200% 이상이 아니면 liquidate 함수를 실행할 수 없다. 마지막에 _revertIfHealthFactorIsBroken를 호출하여 청산자의 담보 비율을 확인하기 때문이다.

    function liquidate(address collateral, address user, uint256 debtToCover)
        external
        moreThanZero(debtToCover)
        nonReentrant
    {
        uint256 startingUserHealthFactor = _healthFactor(user);
        if (startingUserHealthFactor >= MIN_HEALTH_FACTOR) {
            revert DSCEngine__HealthFactorOk();
        }
 
        uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
 
        uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
        uint256 totalCollateralToRedeem = tokenAmountFromDebtCovered + bonusCollateral;
        _redeemCollateral(user, msg.sender, collateral, totalCollateralToRedeem);
@>      _burnDsc(debtToCover, user, msg.sender);
 
        uint256 endingUserHealthFactor = _healthFactor(user);
        if (endingUserHealthFactor <= startingUserHealthFactor) {
            revert DSCEngine__HealthFactorNotImproved();
        }
@>      _revertIfHealthFactorIsBroken(msg.sender);
    }
 
    function _revertIfHealthFactorIsBroken(address user) internal view {
        uint256 userHealthFactor = _healthFactor(user);
@>      if (userHealthFactor < MIN_HEALTH_FACTOR) {
@>          revert DSCEngine__BreaksHealthFactor(userHealthFactor);
        }
    }
 
    function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private {
@>      s_DSCMinted[onBehalfOf] -= amountDscToBurn;
        bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn);
        // This conditional is hypothtically unreachable
        if (!success) {
            revert DSCEngine__TransferFailed();
        }
        i_dsc.burn(amountDscToBurn);
    }    

청산자는 다른 유저를 청산시키기 위해 자신의 토큰을 사용한다. 이 토큰을 사용한다고 해서 청산자의 담보가 줄어드는 것은 아니다. 또한 청산자가 민팅했던 DSC 토큰 기록이 줄어드는 것도 아니다. (청산 대상자의 기록에서 빠진다.)

따라서 청산자의 담보 비율은 확인할 필요가 없다. 청산자의 담보에는 아무 변화가 없기 때문이다. 담보가 적은 이가 자신의 DSC를 이용해 다른 유저를 청산시킬 수 없다는 요구사항도 없다.

Impact

담보가 부족한 유저는 다른 유저를 청산시킬 수 없게 부당하게 제한된다.

Mitigation

liquidate 함수에서 _revertIfHealthFactorIsBroken(msg.sender); 코드를 삭제한다.


tags: bughunting, codehawks, smart contract, solidity, business-logic-vul, severity medium