DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Reentrancy vulnerability in `SoulboundProfileNFT.mintProfile()` allows multiple NFT minting

Description:

The mintProfile() function in SoulboundProfileNFT.sol is vulnerable to reentrancy attacks due to state updates occurring after the _safeMint() call, which includes a callback to the recipient. This callback could be exploited by a malicious contract to mint multiple NFTs before the state variables are updated.

// SoulboundProfileNFT.sol
function mintProfile(string memory name, uint8 age, string memory profileImage) external {
require(profileToToken[msg.sender] == 0, "Profile already exists");
uint256 tokenId = ++_nextTokenId;
_safeMint(msg.sender, tokenId); // Vulnerable external call
// State updates after external call
_profiles[tokenId] = Profile(name, age, profileImage);
profileToToken[msg.sender] = tokenId;
emit ProfileMinted(msg.sender, tokenId, name, age, profileImage);
}
// ERC721.sol from OZ
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data);
}

While the function includes a check require(profileToToken[msg.sender] == 0), this check can be bypassed during reentrancy since profileToToken mapping is updated after the external call.

Impact:

  • Attacker could create multiple profiles, undermining the "one-address-one-profile" principle

  • Inconsistent state in profileToToken mapping

  • burnProfile() and blockProfile() functions would only work with the latest profile

  • Broken profile management system

Recommended Mitigation:

  • Implement Checks-Effects-Interactions pattern

    function mintProfile(string memory name, uint8 age, string memory profileImage) external nonReentrant {
    require(profileToToken[msg.sender] == 0, "Profile already exists");
    uint256 tokenId = ++_nextTokenId;
    // Update state before external call
    _profiles[tokenId] = Profile(name, age, profileImage);
    profileToToken[msg.sender] = tokenId;
    // External call after state updates
    _safeMint(msg.sender, tokenId);
    emit ProfileMinted(msg.sender, tokenId, name, age, profileImage);
    }
  • Add ReentrancyGuard from OZ

Updates

Appeal created

n0kto Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_mintProfile_reentrancy

Likelihood: High, anyone can do it. Impact: Low, several profile will be minted, which is not allowed by the protocol, but only the last one will be stored in profileToToken and won't affect `likeUser` or `matchRewards`.

Support

FAQs

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