Revert Lend - Giorgio's results

A lending protocol specifically designed for liquidity providers on Uniswap v3.

General Information

Platform: Code4rena

Start Date: 04/03/2024

Pot Size: $88,500 USDC

Total HM: 31

Participants: 105

Period: 11 days

Judge: ronnyx2017

Total Solo HM: 7

Id: 342

League: ETH

Revert

Findings Distribution

Researcher Performance

Rank: 7/105

Findings: 4

Award: $1,456.82

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: Bauchibred

Also found by: Giorgio, grearlake, kodyvim

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
upgraded by judge
:robot:_127_group
duplicate-127

Awards

737.0773 USDC - $737.08

External Links

Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Oracle.sol#L368-L375

Vulnerability details

Impact

Attacker will take advantage of inacurate prices returned for negative ticks.

Proof of Concept

The _getReferencePoolPriceX96 below does not take into account negative ticks.

            uint32[] memory secondsAgos = new uint32[](2);
            secondsAgos[0] = 0; // from (before)
            secondsAgos[1] = twapSeconds; // from (before)
            (int56[] memory tickCumulatives,) = pool.observe(secondsAgos); // pool observe may fail when there is not enough history available (only use pool with enough history!)
     @>     int24 tick = int24((tickCumulatives[0] - tickCumulatives[1]) / int56(uint56(twapSeconds))); //@note here can be negative 
            sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick);

This oversight poses a risk, especially when (tickCumulatives[1] - tickCumulatives[0]) % secondsAgo != 0, potentially resulting in a tick value that is larger than it should be. Such discrepancies can create opportunities for price manipulations and arbitrage.

To address this issue, it's crucial to ensure that negative tick values are properly handled and rounded down to avoid inaccuracies in determining the tick value. This correction will enhance the accuracy and reliability of the pricing mechanism, reducing the potential for exploitation and manipulation in the system.

Tools Used

Manual review

Copy the uniswap implementation and add this line.

if (tickCumulatives[1] - tickCumulatives[0] < 0 && (tickCumulatives[1] - tickCumulatives[0]) % secondsAgo != 0) tick --;

Assessed type

Math

#0 - c4-pre-sort

2024-03-22T11:04:14Z

0xEVom marked the issue as duplicate of #498

#1 - c4-pre-sort

2024-03-22T11:04:17Z

0xEVom marked the issue as sufficient quality report

#2 - c4-pre-sort

2024-03-25T12:11:50Z

0xEVom marked the issue as duplicate of #127

#3 - c4-judge

2024-04-01T08:25:19Z

jhsagd76 marked the issue as satisfactory

#4 - c4-judge

2024-04-01T15:41:25Z

jhsagd76 changed the severity to 3 (High Risk)

Findings Information

🌟 Selected for report: Giorgio

Also found by: thank_you

Labels

bug
2 (Med Risk)
primary issue
satisfactory
selected for report
sponsor confirmed
sufficient quality report
:robot:_389_group
M-09

Awards

709.7782 USDC - $709.78

External Links

Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L741-L743 https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L1078-L1080

Vulnerability details

When performing liquidation the liquidator will fill the LiquidateParams containing the liquidation details. The issue here is that instead of sending the liquidation rewards to the LiquidateParams.recipient, the rewards will be sent to msg.sender.

Impact

The liquidation rewards will be sent to msg.sender instead of the recipient, any external logic that relies on the fact that the liquidation rewards will be sent to recipient won't hold, this will influence the protocol's composability.

Proof of Concept

In order to keep the system safe the liquidator can and is incentivised to liquidate unhealthy positions. To do so the liquidate() function will be fired with the appropriate parameters. One of those parameters is the address recipient;, the name is quite intuitive for this one, this is where the liquidation rewards are expected to sent.

But If we follow the liquidation() function logic, the rewards will not be sent to recipient address. This piece of code handles the reward distribution.

(amount0, amount1) = _sendPositionValue(params.tokenId, state.liquidationValue, @> state.fullValue, state.feeValue, msg.sender);

We can see that msg.sender is being used instead of params.recipient.

Tools Used

Manual review

The mitigation is straight forward.

Use params.recipient instead of msg.sender for that specific call.


     (amount0, amount1) =
            _sendPositionValue(params.tokenId, state.liquidationValue, 
 --    state.fullValue, state.feeValue, msg.sender); 
 ++    state.fullValue, state.feeValue, params.recipient); 

Assessed type

Context

#0 - c4-pre-sort

2024-03-20T13:17:29Z

0xEVom marked the issue as duplicate of #426

#1 - c4-pre-sort

2024-03-20T13:17:50Z

0xEVom marked the issue as sufficient quality report

#2 - c4-judge

2024-04-01T06:52:26Z

jhsagd76 marked the issue as not a duplicate

#3 - c4-judge

2024-04-01T06:52:32Z

jhsagd76 marked the issue as primary issue

#4 - c4-judge

2024-04-01T06:53:10Z

jhsagd76 marked the issue as satisfactory

#5 - c4-judge

2024-04-01T15:34:11Z

jhsagd76 marked the issue as selected for report

#6 - c4-sponsor

2024-04-01T20:37:25Z

kalinbas (sponsor) confirmed

#7 - kalinbas

2024-04-09T21:58:23Z

Awards

3.3501 USDC - $3.35

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
sufficient quality report
:robot:_45_group
duplicate-222

External Links

Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L702-L705

Vulnerability details

Liquidation are a way to keep the protocol in a healthy state and clear up bad debt. If users positions are unhealthy other users are entitled to liquidate them, but can they ? The debtor can avoid being liquidated by repaying an insignificant amount while still maintaining an unhealthy position.

Impact

Impossible liquidation, mounting bad debt, and looming insolvency threatens the protocol.

Proof of Concept

When initiating a liquidation, the protocol will asses the debt shares that the position holds.

uint256 debtShares = loans[params.tokenId].debtShares;

However if these exactly don't match with the debt that the liquidator provided, the process will revert.

if (debtShares != params.debtShares) { revert DebtChanged(); }

This is problematic because an malicious user could front run the liquidation transaction by adding 1 wei collateral to the position to deny liquidation.

This exploit can be achieved through the repay()

    function repay(uint256 tokenId, uint256 amount, bool isShare) external override {
        _repay(tokenId, amount, isShare, "");
    }

The calls the _repay() function

       uint256 loanDebtShares = loan.debtShares - shares;
  @>    loan.debtShares = loanDebtShares;
        debtSharesTotal -= shares;

enabling the exploiter to change the loan.debtShares value, and consequentially making the liquidation process revert because debtShares != params.debtShares.

Tools Used

Manual review

In order to mitigate this issue, it is best advised to remove this check :

if (debtShares != params.debtShares) { revert DebtChanged(); }

As long as the position is unhealthy it should be liquidatable.

Assessed type

MEV

#0 - c4-pre-sort

2024-03-18T18:13:59Z

0xEVom marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-03-18T18:14:59Z

0xEVom marked the issue as duplicate of #231

#2 - c4-pre-sort

2024-03-22T12:02:48Z

0xEVom marked the issue as duplicate of #222

#3 - c4-judge

2024-03-31T14:47:29Z

jhsagd76 changed the severity to 2 (Med Risk)

#4 - c4-judge

2024-03-31T16:05:50Z

jhsagd76 marked the issue as satisfactory

Awards

6.6125 USDC - $6.61

Labels

bug
2 (Med Risk)
downgraded by judge
insufficient quality report
partial-50
:robot:_73_group
duplicate-175

External Links

Lines of code

https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Oracle.sol#L363-L368

Vulnerability details

When implemented correctly, the twap oracle makes it very expensive to manipulate prices, because it will aggregate prices over time. In the current implementation twap periods are lose and may be set 0. This will expose the oracle to easy flash loan manipulations and should never happen.

Impact

Price can be manipulated, exploiting the pool and other liquidity providers

Proof of Concept

Here,

if (twapSeconds == 0) { (sqrtPriceX96,,,,,,) = pool.slot0(); } else {

In this bit of code the protocol uses slot0 to access the price data which is very easy to manipulate. An attacker could manipulate the price with a flashloan sell assets at an inflated price and destroy the protocol.

Tools Used

Manual review

Do not use slot0 for price fetching, and use a meaningful twapSeconds, such as 1800, 30 minutes, which is a standard. I saw in the tests that 60 secs were being used which is better than slot0 but still easely maniable(cheap compared to 30 minutes).

Assessed type

Oracle

#0 - c4-pre-sort

2024-03-22T07:55:57Z

0xEVom marked the issue as duplicate of #191

#1 - c4-pre-sort

2024-03-22T07:55:59Z

0xEVom marked the issue as insufficient quality report

#2 - c4-judge

2024-03-31T14:28:19Z

jhsagd76 marked the issue as duplicate of #175

#3 - c4-judge

2024-03-31T14:44:42Z

jhsagd76 marked the issue as partial-50

#4 - c4-judge

2024-04-01T15:42:31Z

jhsagd76 changed the severity to 2 (Med Risk)

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter