Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: high
Invalid

Unchecked Claim Amounts in claimLSDTokens Function

Summary

The claimLSDTokens function in the PriorityPool contract lacks proper validation to ensure that users cannot claim more liquid staking tokens (LSD) than they are entitled to. This vulnerability can lead to unauthorized token claims, resulting in financial losses and unfair distribution among users.

Vulnerability Details

The function does not check whether the _amount and _sharesAmount parameters are greater than the amounts already claimed by the user. This allows users to repeatedly claim tokens without restriction.

https://github.com/Cyfrin/2024-09-stakelink/blob/f5824f9ad67058b24a2c08494e51ddd7efdbb90b/contracts/core/priorityPool/PriorityPool.sol#L365-L388

function claimLSDTokens(
uint256 _amount,
uint256 _sharesAmount,
bytes32[] calldata _merkleProof
) external {
address account = msg.sender;
bytes32 node = keccak256(
bytes.concat(keccak256(abi.encode(account, _amount, _sharesAmount)))
);
if (!MerkleProofUpgradeable.verify(_merkleProof, merkleRoot, node)) revert InvalidProof();
uint256 amountToClaim = _amount - accountClaimed[account];
uint256 sharesAmountToClaim = _sharesAmount - accountSharesClaimed[account];
uint256 amountToClaimWithYield = stakingPool.getStakeByShares(sharesAmountToClaim);
if (amountToClaimWithYield == 0) revert NothingToClaim();
@=> accountClaimed[account] = _amount;
@=> accountSharesClaimed[account] = _sharesAmount;
IERC20Upgradeable(address(stakingPool)).safeTransfer(account, amountToClaimWithYield);
emit ClaimLSDTokens(account, amountToClaim, amountToClaimWithYield);
}

The lack of checks before updating accountClaimed[account] and accountSharesClaimed[account] allows users to claim more than they are entitled to.

Exploitation: A malicious user can repeatedly call claimLSDTokens with the same _amount and _sharesAmount, bypassing the intended limits and claiming excess tokens.

Scenario:

  1. Initial Claim: User A is entitled to claim 100 tokens. They call claimLSDTokens with _amount = 100 and _sharesAmount corresponding to 100 tokens.

  2. Repeated Claims: User A calls the function again with the same parameters. Due to the lack of checks, they successfully claim another 100 tokens.

  3. Result: User A receives 200 tokens instead of the 100 they are entitled to, depleting the pool and affecting other users' claims.

Impact

Malicious users can get more than they should.

Tools Used

Manual review

Recommendations

Add validation checks to ensure that users cannot claim more than they are entitled to.

function claimLSDTokens(
uint256 _amount,
uint256 _sharesAmount,
bytes32[] calldata _merkleProof
) external {
address account = msg.sender;
bytes32 node = keccak256(
bytes.concat(keccak256(abi.encode(account, _amount, _sharesAmount)))
);
if (!MerkleProofUpgradeable.verify(_merkleProof, merkleRoot, node)) revert InvalidProof();
// Add validation to prevent over-claiming
+ if (_amount <= accountClaimed[account] || _sharesAmount <= accountSharesClaimed[account]) {
+ revert InvalidClaimAmounts();
}
uint256 amountToClaim = _amount - accountClaimed[account];
uint256 sharesAmountToClaim = _sharesAmount - accountSharesClaimed[account];
uint256 amountToClaimWithYield = stakingPool.getStakeByShares(sharesAmountToClaim);
if (amountToClaimWithYield == 0) revert NothingToClaim();
accountClaimed[account] = _amount;
accountSharesClaimed[account] = _sharesAmount;
IERC20Upgradeable(address(stakingPool)).safeTransfer(account, amountToClaimWithYield);
emit ClaimLSDTokens(account, amountToClaim, amountToClaimWithYield);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!