Lucene search

K
code423n4Code4renaCODE423N4:2023-10-CANTO-FINDINGS-ISSUES-266
HistoryOct 06, 2023 - 12:00 a.m.

Unvalidated ticks in claimConcentratedRewards allow unauthorized users to claim undeserved rewards. Validate ticks.

2023-10-0600:00:00
Code4rena
github.com
2
claimconcentratedrewards
unauthorized users
undeserved rewards
validation
ticks
liquidity mining
security vulnerability
contract
lowertick
uppertick
validation checks
exploit
mitigation steps

AI Score

6.8

Confidence

High

Lines of code

Vulnerability details

Impact

There is no check that the ticks passed into claimConcentratedRewards actually match the position’s ticks. A user could pass in arbitrary ticks to try to claim rewards for liquidity they don’t own.

Proof of Concept

The claimConcentratedRewards function allows users to claim mining rewards for a specific position they hold, identified by the lowerTick and upperTick range.

Here’s the vulnerability related to validating the tick inputs in the claimConcentratedRewards function.

function claimConcentratedRewards(
    address payable owner, 
    bytes32 poolIdx,
    int24 lowerTick,
    int24 upperTick,
    uint32[] memory weeksToClaim
) internal {

  // Lookup position based on passed ticks
  RangePosition72 storage pos = lookupPosition(
      owner, 
      poolIdx,
      lowerTick,  
      upperTick
  );

  // Rest of reward calculation and transfer

The lowerTick and upperTick passed into the function are used to look up the position via lookupPosition.

However, there is no validation that these match the actual ticks of the position stored in the contract’s mapping.

This means a malicious user could pass in arbitrary tick values that don’t match their position, and claim rewards for liquidity they don’t actually own.

To clarify, here’s a simplified proof of concept demonstrating the lack of tick validation:

// Simplified contract with the same vulnerability
contract LiquidityMining {
    struct RangePosition72 {
        int24 lowerTick;
        int24 upperTick;
    }

    mapping(address => mapping(bytes32 => RangePosition72)) positions;

    function claimConcentratedRewards(
        address payable owner, 
        bytes32 poolIdx,
        int24 lowerTick,
        int24 upperTick,
        uint32[] memory weeksToClaim
    ) internal {
        // Lookup position based on passed ticks
        RangePosition72 storage pos = positions[owner][poolIdx];

        // Rest of reward calculation and transfer
    }
}

The claimConcentratedRewards function retrieves the position based on the passed lowerTick and upperTick without validating whether these ticks match the position’s ticks stored in the mapping of the position.

To exploit this, a user could call claimConcentratedRewards with arbitrary lowerTick and upperTick values, potentially claiming rewards for liquidity they don’t own. The contract lacks the validation checks to ensure that the provided ticks match the actual position.

Tools Used

Vs

Recommended Mitigation Steps

The ticks should be validated against the position, the contract should validate lowerTick and upperTick match the position’s ticks after looking it up:

// Lookup position 
RangePosition72 storage pos = lookupPosition(/*...*/);

// Validate ticks match position  
require(pos.lowerTick == lowerTick, "Invalid lower tick");
require(pos.upperTick == upperTick, "Invalid upper tick");

// Rest of function

Adding this tick validation prevents the attack vector where users can claim arbitrary liquidity rewards.

Assessed type

Invalid Validation


The text was updated successfully, but these errors were encountered:

All reactions

AI Score

6.8

Confidence

High