Snowman Merkle Airdrop

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

Missing nonce and claim status validation enables signature replay attacks

Description:

The SnowmanAirdrop contract contains critical flaws in its signature validation and claim status management that enable signature replay attacks. The contract suffers from two primary vulnerabilities: (1) the SnowmanClaim struct lacks a nonce field for preventing signature reuse, and (2) the claimSnowman() function fails to check if a user has already claimed their airdrop before processing the claim.

The signature structure only includes address receiver and uint256 amount, with no replay protection mechanism. Additionally, while the contract sets s_hasClaimedSnowman[receiver] = true after a successful claim, it never validates this state at the beginning of the function. The contract dynamically calculates the claim amount based on the user's current Snow token balance, which creates an opportunity for exploitation when combined with the missing replay protections.

Attack path:

  1. User legitimately claims their airdrop (e.g., 10 Snow tokens for 10 NFTs)

  2. User's Snow tokens are transferred to the contract during the claim process

  3. Contract sets s_hasClaimedSnowman[user] = true but this is never checked in subsequent calls

  4. User acquires (or received) the exact same amount of Snow tokens they originally had (10 tokens)

  5. User calls claimSnowman() again with the same signature and merkle proof

  6. Contract validates the signature against current balance (10 tokens = original amount)

  7. Merkle proof verification passes since the leaf hash matches (user, 10)

  8. User receives another 10 NFTs despite having already claimed

  9. Process can be repeated multiple times if user continues to restore their Snow token balance

Impact:

Users can claim significantly more NFTs than intended by the protocol design

Recommended Mitigation:

Validate if the user already claimed

function claimSnowman(
address receiver,
bytes32[] calldata merkleProof,
uint8 v, bytes32 r, bytes32 s
) external nonReentrant {
require(!s_hasClaimedSnowman[receiver], "Already claimed");
// ... rest of validation and execution logic
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Lack of claim check

The claim function of the Snowman Airdrop contract doesn't check that a recipient has already claimed a Snowman. This poses no significant risk as is as farming period must have been long concluded before snapshot, creation of merkle script, and finally claiming.

Support

FAQs

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