DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Invalid

High Gas Costs Due to Unbounded Metadata Strings can lead to Denial of Service

Summary

The SoulboundProfileNFT contract is vulnerable to a gas-based denial of service attack where malicious users can create profiles with extremely long names, significantly increasing gas costs for reading profile metadata.

Vulnerability Details

The mintProfile function allows users to input name and profileImage strings of arbitrary length. These strings are stored in the _profiles mapping and later used in the tokenURI function to generate on-chain metadata. When tokenURI is called, the contract encodes the metadata (including the long strings) into a Base64-encoded JSON string. This process consumes significant gas, especially for excessively long strings, leading to potential denial of service (DoS) for systems that rely on reading metadata.

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);
// Store metadata on-chain
_profiles[tokenId] = Profile(name, age, profileImage); // No length checks on `name` or `profileImage`
profileToToken[msg.sender] = tokenId;
emit ProfileMinted(msg.sender, tokenId, name, age, profileImage);
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (ownerOf(tokenId) == address(0)) {
revert ERC721Metadata__URI_QueryFor_NonExistentToken();
}
string memory profileName = _profiles[tokenId].name; // Long strings increase gas costs
uint256 profileAge = _profiles[tokenId].age;
string memory imageURI = _profiles[tokenId].profileImage; // Long strings increase gas costs
return string(
abi.encodePacked(
_baseURI(),
Base64.encode(
bytes(
abi.encodePacked(
'{"name":"',
profileName,
'", ',
'"description":"A soulbound dating profile NFT.", ',
'"attributes": [{"trait_type": "Age", "value": ',
Strings.toString(profileAge),
"}], ",
'"image":"',
imageURI,
'"}'
)
)
)
)
);
}

Poc

function testGasAttackWithLongName() public {
// Mint normal profile
vm.prank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
// Create very long name
string memory longName = "";
for(uint i = 0; i < 1000; i++) {
longName = string.concat(longName, "a");
}
// Mint profile with long name
vm.prank(address(0x456));
soulboundNFT.mintProfile(longName, 25, "ipfs://profileImage");
// Measure tokenURI gas for normal profile
uint256 gas1 = gasleft();
soulboundNFT.tokenURI(1);
uint256 normalURIGas = gas1 - gasleft();
// Measure tokenURI gas for long name profile
uint256 gas2 = gasleft();
soulboundNFT.tokenURI(2);
uint256 longURIGas = gas2 - gasleft();
console.log("Normal tokenURI gas:", normalURIGas);
console.log("Long name tokenURI gas:", longURIGas);
console.log("Gas increase:", longURIGas - normalURIGas);
assertTrue(longURIGas > normalURIGas, "TokenURI gas should increase with long name");
}

Output log

[PASS] testGasAttackWithLongName() (gas: 2012522)
Logs:
Normal tokenURI gas: 17308
Long name tokenURI gas: 104569
Gas increase: 87261 #About six times the gas cost for short profile names

Impact

Attackers can Create multiple profiles with extremely long names Making the protocol expensive to use or cause reverts when the protocol is out of gas due to high gas usage

Tools Used

manual review

Recommendations

Add checks in mintProfile to restrict name and profileImage to reasonable lengths (e.g., 100 characters).

Updates

Appeal created

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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