Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

User can claim rewards multiple times

Summary

The absence of a mechanism to verify whether a user has previously claimed rewards allows them to claim rewards multiple times.

Vulnerability Details

When a user claims rewards through MerkleAirdrop::claim, there is no counter to check the number of times the user has claimed the token. This vulnerability enables attackers to claim rewards more than once. The following proof-of-concept demonstrates this issue, where a user can claim the reward four times:

function testUserCanClaimMultipleTimes() public {
uint256 startingBalance;
uint256 endingBalance;
startingBalance = token.balanceOf(collectorOne);
vm.deal(collectorOne, airdrop.getFee() * 4);
vm.prank(collectorOne);
airdrop.claim{value: airdrop.getFee()}(collectorOne, amountToCollect, proof);
vm.prank(collectorOne);
airdrop.claim{value: airdrop.getFee()}(collectorOne, amountToCollect, proof);
vm.prank(collectorOne);
airdrop.claim{value: airdrop.getFee()}(collectorOne, amountToCollect, proof);
vm.prank(collectorOne);
airdrop.claim{value: airdrop.getFee()}(collectorOne, amountToCollect, proof);
endingBalance = token.balanceOf(collectorOne);
assertEq(endingBalance - startingBalance, amountToCollect * 4);
}

Impact

If an attacker claims the token multiple times, other users will be unable to claim the token as expected. An attacker can claim rewards up to four times, preventing other users from claiming any rewards.

Tools Used

Manual Review

Recommendations

Implement a mechanism to verify whether a user has previously claimed the reward before allowing them to claim it again.

contract MerkleAirdrop is Ownable {
using SafeERC20 for IERC20;
...
uint256 private constant FEE = 1e9;
IERC20 private immutable i_airdropToken;
bytes32 private immutable i_merkleRoot;
+ mapping(address=>uint256) isClaimed;
...
function claim(address account, uint256 amount, bytes32[] calldata merkleProof) external payable {
if (msg.value != FEE) {
revert MerkleAirdrop__InvalidFeeAmount();
}
+ require(!isClaimedp[msg.sender], "Already claimed before");
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(account, amount))));
if (!MerkleProof.verify(merkleProof, i_merkleRoot, leaf)) {
revert MerkleAirdrop__InvalidProof();
}
+ isClaimed[msg.sender] = true;
emit Claimed(account, amount);
i_airdropToken.safeTransfer(account, amount);
}
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

multi-claim-airdrop

Support

FAQs

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