Summary
A blocked user can bypass the intended restriction by recreating their profile. When an NFT owner blocks a user, the corresponding NFT is burned, but the user can still mint a new profile from the same blacklisted address. This loophole undermines the blacklist mechanism.
Impact
The blacklist functionality fails to prevent blocked users from participating in the system. This allows malicious actors to continuously recreate their profiles despite being explicitly blacklisted, defeating the security and access control mechanisms of the protocol.
Recommendation
The contract should enforce blacklist restrictions at the profile creation stage. The following changes ensure that blacklisted users cannot mint new profiles:
+ mapping(address => bool) public isBlocked;
function blockProfile(address blockAddress) external onlyOwner {
uint256 tokenId = profileToToken[blockAddress];
require(tokenId != 0, "No profile found");
+ isBlocked[blockAddress] = true;
_burn(tokenId);
delete profileToToken[blockAddress];
delete _profiles[tokenId];
emit ProfileBurned(blockAddress, tokenId);
}
function mintProfile(string memory name, uint8 age, string memory profileImage) external {
require(profileToToken[msg.sender] == 0, "Profile already exists");
+ require(!isBlocked[msg.sender], "User is blocked");
uint256 tokenId = ++_nextTokenId;
_safeMint(msg.sender, tokenId);
_profiles[tokenId] = Profile(name, age, profileImage);
profileToToken[msg.sender] = tokenId;
emit ProfileMinted(msg.sender, tokenId, name, age, profileImage);
}
Proof of Concept (PoC)
The following test case demonstrates that a blacklisted user can still create a profile, highlighting the issue:
function test__blockedAddressCanRecreateProfile() public {
address malUser = makeAddr("malUser");
vm.prank(malUser);
soulboundNFT.mintProfile("malUser", 12, "ipfs://abcd");
console.log("Malicious user token ID after creating profile:", soulboundNFT.profileToToken(malUser));
assert(soulboundNFT.profileToToken(malUser) != 0);
vm.startPrank(owner);
soulboundNFT.blockProfile(malUser);
assert(soulboundNFT.profileToToken(malUser) == 0);
vm.stopPrank();
console.log("Malicious user token ID after being blocked:", soulboundNFT.profileToToken(malUser));
vm.prank(malUser);
soulboundNFT.mintProfile("malUser", 12, "ipfs://abcd");
assert(soulboundNFT.profileToToken(malUser) != 0);
console.log("Malicious user token ID after recreating profile despite being blocked:", soulboundNFT.profileToToken(malUser));
}
Test Output Logs
Logs:
Malicious user token ID after creating profile: 1
Malicious user token ID after being blocked: 0
Malicious user token ID after recreating profile despite being blocked: 2