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

Anyone can claim the airdrop, as long the claim period is passed

Summary

The claim function in the Airdrop contract has a vulnerability that allows users to claim LoveTokens without verifying Soulmate NFT ownership. Additionally, the current implementation lacks proper checks for the claim period, enabling potential malicious users to exploit the contract and claim LoveTokens without the necessary ownership requirements.

Vulnerability Details

Lack of Soulmate NFT Ownership Check

The claim function does not include a check to ensure that the caller owns a Soulmate NFT. Without this check, any address can call the function and claim LoveTokens, violating the intended behavior described in the function comment.

Inadequate Claim Period Verification

The existing claim period check is insufficient. The current implementation relies on the calculation of the number of days since the last claim, but it does not ensure that enough time has passed since the user's Soulmate NFT creation. As a result, malicious users can exploit this loophole to claim LoveTokens even if they do not meet the necessary ownership criteria.

Impact

Malicious actors could exploit these issues to claim LoveTokens without the required Soulmate NFT ownership, leading to financial losses and undermining the fairness of the distribution mechanism.

POC

  • Copy the below test and run it via cmd forge test --match-test testClaimByHacker -vvvv

function testClaimByHacker() public {
address hacker = makeAddr("hacker");
uint256 initialBalance = loveToken.balanceOf(address(airdropVault));
vm.warp(block.timestamp + 200 days + 1 seconds);
vm.prank(hacker);
airdropContract.claim();
uint256 afterBalance = loveToken.balanceOf(address(airdropVault));
assertGt(initialBalance, afterBalance);
assertGt(loveToken.balanceOf(hacker), 0);
}

Result:

Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.91ms
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommendations

  1. Implement Soulmate NFT Ownership Check:
    Include a check in the claim function to ensure that the caller owns a Soulmate NFT before proceeding with the LoveToken claim process.

  2. Enhance Claim Period Verification:
    Improve the claim period verification by considering the time since the user's Soulmate NFT creation rather than only the time since the last claim. This ensures that users can claim LoveTokens only once per day, in line with the intended functionality.

For example the new code should look like this so that it has the required checks:

function claim() public {
// Check if the caller owns a Soulmate NFT
if (!soulmateContract.ownerOf(msg.sender)) {
revert Airdrop__NotSoulmateNFTOwner();
}
// Check if the couple is divorced
if (soulmateContract.isDivorced()) {
revert Airdrop__CoupleIsDivorced();
}
// Calculate the number of days since the last claim
uint256 numberOfDaysSinceLastClaim = (block.timestamp - lastClaim[msg.sender]) / daysInSecond;
// Check if the claim period has passed (e.g., 1 day)
if (numberOfDaysSinceLastClaim < 1) {
revert Airdrop__ClaimFrequencyExceeded();
}
// Calculate the number of days the couple has been reunited
uint256 numberOfDaysInCouple = (block.timestamp - soulmateContract.idToCreationTimestamp(soulmateContract.ownerToId(msg.sender))) / daysInSecond;
// Calculate the claimable token amount
uint256 amountAlreadyClaimed = _claimedBy[msg.sender];
uint256 tokenAmountToDistribute = (numberOfDaysInCouple * 10 ** loveToken.decimals()) - amountAlreadyClaimed;
// Dust collector
if (tokenAmountToDistribute > loveToken.balanceOf(address(airdropVault))) {
tokenAmountToDistribute = loveToken.balanceOf(address(airdropVault));
}
// Update the last claim timestamp
lastClaim[msg.sender] = block.timestamp;
// Update the claimed amount
_claimedBy[msg.sender] += tokenAmountToDistribute;
// Emit the TokenClaimed event
emit TokenClaimed(msg.sender, tokenAmountToDistribute);
// Transfer LoveTokens from the airdropVault to the user
loveToken.transferFrom(address(airdropVault), msg.sender, tokenAmountToDistribute);
}
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-claim-airdrop-without-owning-NFT

High severity, This issue is separated from the flawed `isDivorced()` check presented in issue #168 as even if that is fixed, if ownership is not checked, isDivorced would still default to false and allow bypass to claim airdrops by posing as tokenId 0 in turn resulting in this [important check for token claim is bypassed.](https://github.com/Cyfrin/2024-02-soulmate/blob/b3f9227942ffd5c443ce6bccaa980fea0304c38f/src/Airdrop.sol#L61-L66). #220 is the most comprehensive issue as it correctly recognizes both issues existing within the same function.

Support

FAQs

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