An attacker can mint unlimited NFTs without any restrictions due to missing access control in Snowman::mintSnowman
Description: The mintSnowman function in the Snowman contract lacks any access control mechanism, allowing anyone to mint unlimited NFTs to any address. This completely breaks the airdrop economics and NFT scarcity model.
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++;
}
}
The function processes without any validation of the caller, allowing malicious actors to:
-
Mint millions of NFTs for free
-
Devalue all legitimate NFTs to zero
-
Break the intended airdrop distribution mechanism
-
Perform gas DoS attacks with large mint amounts
Impact: Complete protocol compromise, unlimited NFT minting, total economic failure.
Proof of Concept:
function test_UnrestrictedNFTMinting() public {
uint256 initialSupply = snowman.getTokenCounter();
vm.prank(attacker);
snowman.mintSnowman(attacker, 1000);
uint256 finalSupply = snowman.getTokenCounter();
assertEq(finalSupply, initialSupply + 1000);
assertEq(snowman.ownerOf(0), attacker);
assertEq(snowman.ownerOf(999), attacker);
}
Recommended Mitigation: Implement proper access control with authorized minter pattern:
error SM__NotAuthorized();
mapping(address => bool) private s_authorizedMinters;
modifier onlyAuthorizedMinter() {
if (!s_authorizedMinters[msg.sender] && msg.sender != owner()) {
revert SM__NotAuthorized();
}
_;
}
function mintSnowman(address receiver, uint256 amount) external onlyAuthorizedMinter {
if (receiver == address(0)) revert SM__ZeroAddress();
if (amount == 0) revert SM__ZeroAmount();
for (uint256 i = 0; i < amount; i++) {
_safeMint(receiver, s_TokenCounter);
emit SnowmanMinted(receiver, s_TokenCounter);
s_TokenCounter++;
}
}
function setAuthorizedMinter(address minter, bool authorized) external onlyOwner {
s_authorizedMinters[minter] = authorized;
}