Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: high
Likelihood: low
Invalid

Missing merkle root validation enables potential proof bypass attacks

Description:

The SnowmanAirdrop contract constructor fails to validate that the _merkleRoot parameter is not zero (0x00). While other constructor parameters like _snow and _snowman addresses are properly validated for zero values, the merkle root accepts any input including the zero hash. This creates a critical vulnerability similar to the infamous Nomad Bridge exploit that resulted in $190 million in losses in August 2022.

In the Nomad Bridge incident, a zero value was mistakenly set as a trusted root during initialization, which combined with EVM's default storage initialization behavior (all uninitialized storage slots return 0x0) allowed attackers to bypass merkle proof validation entirely. When MerkleProof.verify() is called against a zero root, certain edge cases or malformed proofs may incorrectly validate as legitimate, breaking the fundamental security assumption of the merkle tree authentication system.

Attack path:

  1. Contract is accidentally deployed with _merkleRoot set to bytes32(0)

  2. Attacker constructs specially crafted merkle proofs that validate against the zero root

  3. Due to EVM storage default values and potential edge cases in merkle proof verification logic, these malformed proofs may pass validation

  4. Attacker calls claimSnowman() with a crafted proof that bypasses MerkleProof.verify(merkleProof, i_merkleRoot, leaf)

  5. If the attacker possesses Snow tokens (which is required by the protocol), they can successfully claim Snowman NFTs they are not entitled to

  6. Multiple attackers can exploit this vulnerability, potentially draining the airdrop allocation

Impact:

Unauthorized users can claim Snowman NFTs without being in the legitimate whitelist

Recommended Mitigation:

Add validation for zero merkle root in the constructor:

constructor(bytes32 _merkleRoot, address _snow, address _snowman) EIP712("Snowman Airdrop", "1") {
if (_merkleRoot == bytes32(0)) {
revert SA__InvalidMerkleRoot();
}
if (_snow == address(0)) {
revert SA__ZeroAddress();
}
if (_snowman == address(0)) {
revert SA__ZeroAddress();
}
i_merkleRoot = _merkleRoot;
i_snow = Snow(_snow);
i_snowman = Snowman(_snowman);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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