Snowman Merkle Airdrop

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

Anyone Can Mint Unlimited NFTs Due to Missing Access Control

The mintSnowman() lacks any access control, allowing any external caller to freely mint an unlimited number of NFTs to any address, which completely undermines the collection's scarcity and intended airdrop mechanism.

Description

  • The protocol is designed around a specific user flow: users acquire Snow (ERC20) tokens and stake them in the SnowmanAirdrop contract. This contract then validates their eligibility (e.g., via Merkle proofs) and calls the Snowman.sol contract to mint the corresponding Snowman (ERC721) NFTs for the user. This makes the SnowmanAirdrop contract the sole, authorized minter.

  • The critical vulnerability is that the mintSnowman function in the Snowman.sol contract is external with no access control. This means any user can call it directly, bypassing the SnowmanAirdrop contract entirely. An attacker can mint any number of NFTs without ever needing to acquire or stake a single Snow token, rendering the core economic and distribution mechanism of the protocol completely useless.

contract Snowman is ERC721, Ownable {
// ...
// >>> EXTERNAL FUNCTIONS
@> function mintSnowman(address receiver, uint256 amount) external {
//@audit no access control, should only be called from snowman airdrop contract
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}
// ...
}

Risk

Likelihood: High

  • Any user who discovers the public mintSnowman function can immediately abuse it.

Impact: High

  • Complete Bypass of Staking and Airdrop Mechanism: The entire intended logic—earning/buying Snow, staking it, and claiming via Merkle proof in SnowmanAirdrop—is made obsolete. Attackers can acquire the final reward (the NFT) without participating in the protocol.

  • Destruction of Snow Token Utility: Since the Snowman NFTs can be minted for free, the Snow token, whose primary purpose is to be staked for these NFTs, loses all its utility and economic value.

Proof of Concept

The following test demonstrates that an attacker, without holding any Snow tokens or interacting with the SnowmanAirdrop contract, can directly call mintSnowman to mint NFTs for free.

function testMintSnowmanForFree() public {
address attacker = makeAddr("attacker");
vm.prank(attacker);
nft.mintSnowman(attacker, 1000);
assert(nft.balanceOf(attacker) == 1000);
}

Recommended Mitigation

Enforce strict access control on the mintSnowman function to ensure it can only be called by the legitimate SnowmanAirdrop contract. This restores the intended protocol flow and protects the integrity of the NFT supply.

contract Snowman is ERC721, Ownable {
// >>> ERROR
error ERC721Metadata__URI_QueryFor_NonExistentToken();
- error SM__NotAllowed();
+ error Snowman__NotAirdropContract();
// >>> VARIABLES
uint256 private s_TokenCounter;
string private s_SnowmanSvgUri;
+ address public s_airdropContract;
// >>> EVENTS
event SnowmanMinted(address indexed receiver, uint256 indexed numberOfSnowman);
+ // >>> MODIFIERS
+ modifier onlyAirdropContract() {
+ if (msg.sender != s_airdropContract) {
+ revert Snowman__NotAirdropContract();
+ }
+ _;
+ }
// >>> CONSTRUCTOR
constructor(string memory _SnowmanSvgUri) ERC721("Snowman Airdrop", "SNOWMAN") Ownable(msg.sender) {
s_TokenCounter = 0;
s_SnowmanSvgUri = _SnowmanSvgUri;
}
// >>> EXTERNAL FUNCTIONS
- function mintSnowman(address receiver, uint256 amount) external {
+ function mintSnowman(address receiver, uint256 amount) external onlyAirdropContract {
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}
+ // Function for the owner to set the authorized airdrop contract address.
+ function setAirdropContract(address _airdropContract) external onlyOwner {
+ s_airdropContract = _airdropContract;
+ }
// ... (rest of the contract)
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 5 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.