Snowman Merkle Airdrop

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

H1-Anyone can mint Snowman NFT (Lack of access control)

Root + Impact

Description

In normal behavior, the mintSnowman function is intended to be called by a trusted contract (such as an airdrop distributor) to mint Snowman NFTs to eligible users based on off-chain logic or Merkle proofs. In this project, this contract is SnowmanAidrop

However, the function is marked external and lacks any form of access control. This allows any arbitrary external address to call mintSnowman, minting an unlimited number of NFTs to themselves or others without restriction.

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

Risk

Likelihood:

  • This will occur any time an attacker directly interacts with the contract and invokes mintSnowman, since there is no modifier or check preventing public access.

  • It does not require any special privileges or prior conditions; a standard external call from a web3 wallet or script is sufficient.

Impact:

  • Unlimited and unauthorized minting of NFTs, leading to total dilution of supply and value.

  • Project credibility may suffer significantly if an attacker exploits this to flood the market

Proof of Concept

Add the following test in the test contract TestSnowman

function testMintSnowmanAccessControl() public {
// Alice mint tokens to herself
vm.prank(alice);
nft.mintSnowman(alice, 1);
assert(nft.ownerOf(0) == alice);
}

Recommended Mitigation

Restrict access to only an authorized minter, such as the airdrop contract:

+ SnowmanAirdrop private immutable i_airdrop;
+ error Unauthorized();
+ modifier onlyAirdrop() {
+ if (msg.sender != address(i_airdrop)) revert Unauthorized();
+ _;
+ }
function mintSnowman(address receiver, uint256 amount)
- external {
+ external onlyAirdrop {
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}

This ensures that only the intended contract (here the airdrop distributor) can mint NFTs, preventing unauthorized access.

Updates

Lead Judging Commences

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