sherlock-audit / 2024-08-sentiment-v2-judging

0 stars 0 forks source link

X12 - Small loans can extend the TVL of any position up to 90% #215

Open sherlock-admin3 opened 3 weeks ago

sherlock-admin3 commented 3 weeks ago

X12

Medium

Small loans can extend the TVL of any position up to 90%

Summary

Users can exploit the minDebt feature to extend their LTV up to 90% for risky assets.

Vulnerability Detail

The system uses a minDebt threshold to ensure that positions are profitable for liquidation. Loans below this amount may not be profitable to liquidate, as liquidators would incur gas fees, increasing their costs.

The repay function checks if the borrowed amount is below minDebt and reverts the TX if it is.

        remainingShares = borrowSharesOf[poolId][position] - borrowShares;
        if (remainingShares > 0) {
            uint256 newBorrowAssets = _convertToAssets(
                remainingShares, pool.totalBorrowAssets - amt, pool.totalBorrowShares - borrowShares, Math.Rounding.Down
            );
            if (_getValueOf(pool.asset, newBorrowAssets) < minDebt) {
                revert Pool_DebtTooLow(poolId, pool.asset, newBorrowAssets);
            }
        }

Liquidators also cannot seize the entire position. They are limited to a maximum of debt repaid * 1e18 / 0.9e18, which is 11% more than what they have to repay. That's their profit.

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/main/protocol-v2/src/RiskModule.sol#L156-L159

        uint256 maxSeizedAssetValue = debtRepaidValue.mulDiv(1e18, (1e18 - discount));
        if (assetSeizedValue > maxSeizedAssetValue) {
            revert RiskModule_SeizedTooMuch(assetSeizedValue, maxSeizedAssetValue);
        }

Users combine the above 2 mechanics and game the system by taking small loans that prevent liquidators from fully liquidating their position due to the minDebt limit, while also making partial liquidation unprofitable.

Example:

  1. A WETH pool has risky collateral - token X with an LTV of 50%.
  2. Alice opens a position with collateral X valued at 0.05 ETH (equal to minDebt).
  3. She borrows 0.25 WETH from the pool.
  4. The asset’s price drops, increasing her LTV to 55%.

Alice won’t be liquidated because any liquidation attempt would leave her position below minDebt, causing the transaction to revert. Liquidators must wait until her LTV reaches 90% to perform a full liquidation (~0.045 debt, for 0.05 col).

Alice can also avoid paying her debt, as the risky asset might very well quickly cross the gap between 90% and 100% and make her position insolvent, causing bad debt. She can abuse this on chains with low fees (ARB, BASE, OP) and create multiple position borrowing from the pool.

Impact

The core LTV mechanism is broken. Users can leverage risky assets with high LTV, increasing the system’s exposure to bad debt.

Code Snippet

        uint256 maxSeizedAssetValue = debtRepaidValue.mulDiv(1e18, (1e18 - discount));
        if (assetSeizedValue > maxSeizedAssetValue) {
            revert RiskModule_SeizedTooMuch(assetSeizedValue, maxSeizedAssetValue);
        }

Tool Used

Manual Review

Recommendation

Allow liquidators to fully liquidate a position if the remaining value is less than minDebt.

iamnmt commented 2 days ago

Escalate

Invalid. Incorrect statement. Incorrect example.

LTV is a constant value, which is set by the pool owner

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/RiskEngine.sol#L207

then the LTV of a collateral is used to calculate the minReqAssetValue of a position

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/RiskModule.sol#L264

The LTV can not be changed as described in the issue.

The example is incorrect. If Alice has 0.05 ETH worth of token X, then she can only borrow up to 0.05 ETH * 50% = 0.025 WETH (assuming 1 WETH = 1 ETH)

  1. She borrows 0.25 WETH from the pool.

The LTV does not change

  1. The asset’s price drops, increasing her LTV to 55%.

This claim is incorrect

Alice won’t be liquidated because any liquidation attempt would leave her position below minDebt, causing the transaction to revert.

Alice can be liquidate by setting debtData[i].amt = type(uint256).max

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/RiskModule.sol#L141

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/PositionManager.sol#L492

then the borrow shares can be repaid completely.

sherlock-admin3 commented 2 days ago

Escalate

Invalid. Incorrect statement. Incorrect example.

LTV is a constant value, which is set by the pool owner

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/RiskEngine.sol#L207

then the LTV of a collateral is used to calculate the minReqAssetValue of a position

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/RiskModule.sol#L264

The LTV can not be changed as described in the issue.

The example is incorrect. If Alice has 0.05 ETH worth of token X, then she can only borrow up to 0.05 ETH * 50% = 0.025 WETH (assuming 1 WETH = 1 ETH)

  1. She borrows 0.25 WETH from the pool.

The LTV does not change

  1. The asset’s price drops, increasing her LTV to 55%.

This claim is incorrect

Alice won’t be liquidated because any liquidation attempt would leave her position below minDebt, causing the transaction to revert.

Alice can be liquidate by setting debtData[i].amt = type(uint256).max

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/RiskModule.sol#L141

https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/0b472f4bffdb2c7432a5d21f1636139cc01561a5/protocol-v2/src/PositionManager.sol#L492

then the borrow shares can be repaid completely.

You've created a valid escalation!

To remove the escalation from consideration: Delete your comment.

You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.

project-X12 commented 1 day ago

The example is incorrect. If Alice has 0.05 ETH worth of token X, then she can only borrow up to 0.05 ETH * 50% = 0.025 WETH (assuming 1 WETH = 1 ETH)

The LTV variables won't change here, but the position will get lower collateral value assigned, moving her closer and even crossing the LTV threshold.

This is because LTV is loan to value and if your value (collateral) is 0.05 with a loan of 0.025 you have LTV of 50%, and if the value of the collateral drops by 5%, like in the example, to 0.0475 with the same loan of 0.025 your new LTV is ~53%.