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:
Initial Claim: User A is entitled to claim 100 tokens. They call claimLSDTokens with _amount = 100 and _sharesAmount corresponding to 100 tokens.
Repeated Claims: User A calls the function again with the same parameters. Due to the lack of checks, they successfully claim another 100 tokens.
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);
}