Snowman Merkle Airdrop

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

Unrestricted Minting Function Without Access Controls

Summary: The mintSnowman function in the Snowman contract lacks any access controls, allowing any address to mint an unlimited number of tokens without restriction.

Description: The Snowman contract implements an ERC721 non-fungible token intended for distribution via a controlled airdrop mechanism. However, the mintSnowman function that creates new tokens does not implement any access controls or restrictions. This function allows any external address to call it and mint an unlimited number of tokens to any recipient address.

The code's behaviour permits any caller to bypass the entire intended airdrop distribution flow. The SnowmanAirdrop contract implements an elaborate claim mechanism that requires users to:

  1. Acquire Snow tokens by paying fees

  2. Provide valid Merkle proofs of eligibility

  3. Submit valid signatures for verification

  4. Burn their Snow tokens to receive Snowman NFTs

With the unrestricted minting vulnerability, an attacker can completely circumvent these requirements and mint unlimited tokens without any cost or validation checks.

To exploit this vulnerability, an attacker simply needs to call the mintSnowman function with their address as the recipient and their desired quantity. The attacker could also mint tokens to any other address, acting as an unauthorized distributor.

Step-by-step Analysis:

  1. The mintSnowman function is declared as external without any modifiers or checks to restrict who can call it.

  2. The function accepts any address as the recipient and any quantity as the amount parameter.

  3. Within a loop, it mints tokens to the specified recipient using the _safeMint function.

  4. The token counter is incremented after each minting, allowing sequential token IDs.

  5. No validation exists to ensure the caller is authorized or has satisfied any requirements.

  6. No limit is imposed on the total supply or the amount that can be minted in a single transaction.

Severity Classification:

  • Impact: High - This vulnerability completely undermines the intended token distribution mechanism, allows unlimited token supply inflation, and removes any economic cost for obtaining the NFTs. It destroys the scarcity and value proposition of the NFT collection.

  • Likelihood: High - Exploitation requires no special skills or conditions. Any address can call this function directly, making it trivial to exploit.

File Name: src/Snowman.sol

Code:

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

Recommendation:

  1. Implement access control to restrict minting to authorized addresses only.

  2. Consider adding a maximum supply limit to maintain scarcity.

  3. Add batch minting limits to prevent excessive gas consumption.

Recommended Code Fix:

// Add a state variable to track authorized minters
mapping(address => bool) private s_authorizedMinters;
// Add a function to manage authorized minters (owner only)
function setAuthorizedMinter(address minter, bool status) external onlyOwner {
s_authorizedMinters[minter] = status;
}
// Update the mintSnowman function with access control
function mintSnowman(address receiver, uint256 amount) external {
// Check if caller is authorized
if (!s_authorizedMinters[msg.sender] && msg.sender != owner()) {
revert SM__NotAllowed();
}
// Optional: Add maximum supply check
if (s_TokenCounter + amount > MAX_SUPPLY) {
revert("Exceeds maximum supply");
}
// Optional: Add reasonable batch size limit
if (amount > MAX_BATCH_SIZE) {
revert("Exceeds maximum batch size");
}
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

This fix would ensure that only authorized addresses (including the contract owner) can mint tokens, and would optionally enforce supply and batch size limits.

Proof of Concept:

// Test case demonstrating unrestricted minting
function test_POC_UnrestrictedMinting() public {
// Any address can mint tokens
address attacker = address(0xDEAD);
vm.prank(attacker);
snowman.mintSnowman(attacker, 100);
// Verify attacker received tokens
assertEq(snowman.balanceOf(attacker), 100);
// Attacker can even mint to other addresses
vm.prank(attacker);
snowman.mintSnowman(address(0xBEEF), 50);
assertEq(snowman.balanceOf(address(0xBEEF)), 50);
}
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.