Snowman Merkle Airdrop

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

Missing access control in mintSnowman enables unlimited free NFT minting

Description:

The mintSnowman function in the Snowman.sol contract lacks any access control mechanisms, allowing any external caller to mint unlimited Snowman NFTs without authorization. The function is marked as external but has no modifiers restricting who can call it, completely bypassing the intended airdrop mechanism that requires users to earn Snow tokens and provide valid Merkle proofs.

In Snowman.sol

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++;
}
}

This function should only be callable by the SnowmanAirdrop contract after proper validation of Merkle proofs, signature verification, and Snow token burning. Instead, it's accessible to any address on the blockchain.

Attack path:

  1. Attacker identifies the deployed Snowman contract address

  2. Attacker calls mintSnowman(attackerAddress, largeAmount) directly

  3. The function executes without any validation, minting unlimited NFTs to the attacker

  4. Attacker can repeat this process indefinitely or distribute NFTs to multiple addresses

  5. The attack requires no Snow tokens, no Merkle proof, and no signature verification

Example attack transaction:

// Mint 1000 NFTs for free
snowmanContract.mintSnowman(0xAttackerAddress, 1000);
// Spam mint to random addresses
for(uint i = 0; i < 1000; i++) {
snowmanContract.mintSnowman(address(uint160(i)), 1);
}

Impact:

The entire airdrop mechanism becomes meaningless as anyone can mint NFTs without earning them

All Snowman NFTs lose value due to unlimited supply inflation

Recommended Mitigation:

Implement proper access control to restrict the mintSnowman function to authorized callers only:

Restrict to airdrop contract only

address public airdropContract;
modifier onlyAirdrop() {
require(msg.sender == airdropContract, "Only airdrop contract can mint");
_;
}
function mintSnowman(address receiver, uint256 amount) external onlyAirdrop {
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}
function setAirdropContract(address _airdropContract) external onlyOwner {
require(_airdropContract != address(0), "Zero address not allowed");
airdropContract = _airdropContract;
}
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.