DatingDapp

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

Storage-Based Denial of Service Attack via Unvalidated Profile Image URLs

Summary

Vulnerability Details

The mintProfile function in the SoulboundProfileNFT contract does not validate the profileImage URL provided by the user. This lack of validation allows attackers to input arbitrarily large URLs, leading to excessive storage consumption on-chain. The test testStorageAttackWithDifferentAttackers demonstrates that attackers can mint profiles with extremely large image URLs (e.g., 100MB), causing the contract to run out of memory (MemoryOOG error). This results in a denial of service (DoS) for legitimate users.

IMPACTS

Severity: High

  • Denial of Service (DoS): Attackers can repeatedly store large profile image URLs, exhausting contract storage and making it impossible for other users to interact with the contract.

  • Increased Gas Costs: Processing and storing large image URLs significantly increases gas costs, making minting prohibitively expensive.

  • Chain Bloat: Unrestricted storage usage leads to unnecessary blockchain state growth, increasing the long-term cost of maintaining the network.

Proof of Concept (PoC)

copy and paste this test into testSoulboundProfileNFT.t.sol

function testStorageAttackWithDifferentAttackers() public {
// Arrange
string memory largeImage = string(
abi.encodePacked(
"https://attacker.com/",
new bytes(100000000) // 100MB of data
)
);
// Act - Multiple attackers minting large images
vm.prank(attacker1);
soulboundNFT.mintProfile("Attacker 1 Profile", 25, largeImage);
vm.prank(attacker2);
soulboundNFT.mintProfile("Attacker 2 Profile", 25, largeImage);
// Assert
uint256 tokenId1 = soulboundNFT.profileToToken(attacker1);
uint256 tokenId2 = soulboundNFT.profileToToken(attacker2);
assertEq(tokenId1, 1, "First attacker should have token 1");
assertEq(tokenId2, 2, "Second attacker should have token 2");
// Verify storage impact
string memory image1 = soulboundNFT.tokenURI(tokenId1);
string memory image2 = soulboundNFT.tokenURI(tokenId2);
assertGt(bytes(image1).length, 1000000, "First stored image should be large");
assertGt(bytes(image2).length, 1000000, "Second stored image should be large");
}
}

Test Result:

Ran 1 test for test/testSoulboundProfileNFT.t.sol:SoulboundProfileNFTTest
[FAIL: EvmError: MemoryOOG] testStorageAttackWithDifferentAttackers() (gas: 1073720760)
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 4.26ms (237.60µs CPU time)
Ran 1 test suite in 31.33ms (4.26ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/testSoulboundProfileNFT.t.sol:SoulboundProfileNFTTest
[FAIL: EvmError: MemoryOOG] testStorageAttackWithDifferentAttackers() (gas: 1073720760)

Tools Used

Manual review and foundry

Recommendations

Implementing strict URL validation, length & image size constraints and off-chain storage solutions can mitigate this vulnerability and prevent a storage-based DoS attack.

Updates

Appeal created

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

invalid_URI_injection_scam_underaged_bad_name_photo_etc

Scamming/phishing is not the protocol problem, that's a user mistake. NFT are unique, even if someone does a copy of your profile (which is also possible in web2), I consider it informational. Injection is a problem for the web2 part of the protocol, not a bug here. For the age, it depends on the countries law and future medicine. Anyways, that's more an ethical/political problem, not a bug.

Users mistake, only impacting themselves.

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood 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.