Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
Submission Details
Impact: high
Likelihood: high

Unrestricted Minting in Snowman Contract

Author Revealed upon completion

Root + Impact

Description

  • Expected: The mintSnowman function should only be called by an authorized source, such as the SnowmanAirdrop contract, after verifying eligibility via Merkle proof and EIP-712 signature. This ensures NFTs are distributed fairly and securely.

  • Bug: Currently, the mintSnowman function is marked as external and lacks any access control. This means any external address can call this function and mint any amount of NFTs arbitrarily, without satisfying any airdrop conditions. This completely bypasses the eligibility verification process.

// ❌ Vulnerable Code
function mintSnowman(address receiver, uint256 amount) external {
for (uint256 i = 0; i < amount; ++i) {
_mint(receiver, totalSupply() + 1);
}
}

Risk

Likelihood:

  • This issue will occur the moment someone inspects the ABI or contract source and sees that minting is publicly accessible.

  • No signature, Merkle proof, ownership, or whitelisting condition is required — any external user can interact with this function.

  • Highly likely to be exploited in production if deployed without controls, especially if tokens have value.

Impact:

  • An attacker can mint unlimited NFTs to themselves, breaking the airdrop logic.

  • It enables sybil-like behavior where users can extract value without participating in intended tokenomics.

  • The NFT collection's value and scarcity can be severely diluted, affecting collector trust and market reputation.


Proof of Concept

// Any external wallet can interact with the contract like this:
Snowman snowman = Snowman(0xDeployedSnowmanAddress);
snowman.mintSnowman(msg.sender, 1000); // Mints 1000 NFTs to self without any restriction

Explanation:
This PoC assumes that the attacker has read the ABI or contract source and sees no restrictions on minting. The attacker can deploy a script or use Remix/ethers.js to call mintSnowman() directly and issue thousands of NFTs to their own address, all without validation or approval.


Recommended Mitigation

- function mintSnowman(address receiver, uint256 amount) external {
+ function mintSnowman(address receiver, uint256 amount) external onlyOwner {
for (uint256 i = 0; i < amount; ++i) {
_mint(receiver, totalSupply() + 1);
}
}

Explanation:
Use the onlyOwner modifier (or a role-based system like OpenZeppelin’s AccessControl) to restrict this function to trusted callers only, such as the Airdrop contract. Ideally, this minting function should be internal or only callable by contracts that perform EIP-712/Merkle verification. This ensures that only legitimate claims result in NFT minting, preserving the intended distribution and value of the collection.

Support

FAQs

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