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

Soulmate NFT is not required to claim airdrop, so anybody who is not divorced can claim LoveToken

Summary

The claim function in the Airdrop.sol contract do not check if the caller has a soulmate NFT, therefore anyone who isn't divorced can claim love tokens. Worst they can claim very large amount of tokens from the protocol, making them insanely rich with love.

Vulnerability Details

Only soulmates should be able to claim airdrop, but there is no checks for soulmate NFT in the Airdrop.sol claim function.

This function only checks for the divorce status of the caller, and initially the divorce status will be false, so any user who isn't divorced can claim LoveToken,
soulmateContract.isDivorced() will return false, if the caller is not divorced and a caller without soulmate is not divorced.

Addresses without Soulmate NFT are able to claim more LoveToken, than Addresses with Soulmate NFT, the numberOfDaysInCouple is used to calculate the amount of token to send to the caller, for a caller with a Soulmate NFT
it start counting from the day the NFT was minted to the day he is calling the claim function.
But for callers without Soulmate NFT, it start counting from January 1st, 1970, to the day the claim function is being called, which is about 54years, so callers without Soulmate NFTs get to collect over 54 years of LoveTokens.

function claim() public {
// No LoveToken for people who don't love their soulmates anymore.
@-> if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
// Calculating since how long soulmates are reunited
@-> uint256 numberOfDaysInCouple = (block.timestamp -
soulmateContract.idToCreationTimestamp(
soulmateContract.ownerToId(msg.sender)
)) / daysInSecond;
uint256 amountAlreadyClaimed = _claimedBy[msg.sender];
if (
amountAlreadyClaimed >=
numberOfDaysInCouple * 10 ** loveToken.decimals()
) revert Airdrop__PreviousTokenAlreadyClaimed();
uint256 tokenAmountToDistribute = (numberOfDaysInCouple *
10 ** loveToken.decimals()) - amountAlreadyClaimed;
// Dust collector
if (
tokenAmountToDistribute >=
loveToken.balanceOf(address(airdropVault))
) {
tokenAmountToDistribute = loveToken.balanceOf(
address(airdropVault)
);
}
_claimedBy[msg.sender] += tokenAmountToDistribute;
emit TokenClaimed(msg.sender, tokenAmountToDistribute);
loveToken.transferFrom(
address(airdropVault),
msg.sender,
tokenAmountToDistribute
);
}

POC

From the above test any random user can mint LoveToken as long as they are not divorced.
Add the test below to AirdropTest.t.sol and use the command below to run the code

forge test --mt test_POC_Can_Claim_Without_Soulmate
function test_POC_Can_Claim_Without_Soulmate(address randomUser) public {
vm.warp(block.timestamp + 200 days + 1 seconds);
vm.deal(randomUser, 1 ether);
assertTrue(loveToken.balanceOf(randomUser) == 0);
vm.prank(randomUser);
airdropContract.claim();
assertTrue(loveToken.balanceOf(randomUser) > 0);
}

Impact

Unauthorized addresses can claim LoveToken from Airdrop.sol contract, and the worst part of it is that, they can claim over 54years of LoveToken, making them extremely rich with love.

Tools Used

Manual Analysis and Foundry

Recommendations

Add checks to comfirm if the caller, has a Soulmate NFT.

function claim() public {
+ // Check if the caller has a soulmate else revert.
+ if (soulmateContract.soulmateOf(msg.sender) == address(0)) revert();
// No LoveToken for people who don't love their soulmates anymore.
if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
...
}
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.