Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Unrestricted NFT Minting - Snowman.sol:36

An attacker can mint unlimited NFTs without any restrictions due to missing access control in Snowman::mintSnowman

Description: The mintSnowman function in the Snowman contract lacks any access control mechanism, allowing anyone to mint unlimited NFTs to any address. This completely breaks the airdrop economics and NFT scarcity model.

function mintSnowman(address receiver, uint256 amount) external {
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

The function processes without any validation of the caller, allowing malicious actors to:

  • Mint millions of NFTs for free

  • Devalue all legitimate NFTs to zero

  • Break the intended airdrop distribution mechanism

  • Perform gas DoS attacks with large mint amounts

Impact: Complete protocol compromise, unlimited NFT minting, total economic failure.

Proof of Concept:

function test_UnrestrictedNFTMinting() public {
// Initial state
uint256 initialSupply = snowman.getTokenCounter();
// Attacker mints 1000 NFTs to themselves - completely unrestricted!
vm.prank(attacker);
snowman.mintSnowman(attacker, 1000);
// Verify the attack succeeded
uint256 finalSupply = snowman.getTokenCounter();
assertEq(finalSupply, initialSupply + 1000);
assertEq(snowman.ownerOf(0), attacker);
assertEq(snowman.ownerOf(999), attacker);
}

Recommended Mitigation: Implement proper access control with authorized minter pattern:

error SM__NotAuthorized();
mapping(address => bool) private s_authorizedMinters;
modifier onlyAuthorizedMinter() {
if (!s_authorizedMinters[msg.sender] && msg.sender != owner()) {
revert SM__NotAuthorized();
}
_;
}
function mintSnowman(address receiver, uint256 amount) external onlyAuthorizedMinter {
if (receiver == address(0)) revert SM__ZeroAddress();
if (amount == 0) revert SM__ZeroAmount();
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}
function setAuthorizedMinter(address minter, bool authorized) external onlyOwner {
s_authorizedMinters[minter] = authorized;
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Unrestricted NFT mint function

The mint function of the Snowman contract is unprotected. Hence, anyone can call it and mint NFTs without necessarily partaking in the airdrop.

Support

FAQs

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