Snowman Merkle Airdrop

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

Merkle Proof Malleability

Root + Impact

Description

  • Expected : The Merkle proof verification in claimSnowman should generate a unique leaf hash for each (receiver, amount) pair to ensure only eligible users can claim tokens.

  • Bug : The leaf node is constructed using nested hashing : keccak256(bytes.concat(keccak256(abi.encode(receiver, amount)))). This creates malleable proofs where different inputs may produce the same hash, enabling forged claims.

// ❌ Vulnerable Code
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(receiver, amount))));

Risk

Likelihood :

  • Medium : Requires an attacker to find hash collisions or exploit encoding ambiguities in the nested hash structure.

  • Medium : Depends on how the Merkle root is generated off-chain; improper handling increases exploitability.

Impact :

  • High : Allows attackers to claim tokens without valid Merkle proof by crafting malicious (receiver, amount) pairs that match existing leaf hashes.

  • Medium : Could lead to unauthorized token issuance and dilution of the airdrop pool.

Proof of Concept

// Exploit contract demonstrating Merkle proof malleability
contract Exploit {
address public snowmanAirdrop = 0xDeployedAirdropAddress;
function attack() external {
// Craft a malicious (receiver, amount) pair that collides with a valid leaf hash
address receiver = address(0x1234);
uint256 amount = 100;
// Compute leaf hash with nested hashing
bytes32 leaf = keccak256(
bytes.concat(keccak256(abi.encode(receiver, amount)))
);
// Generate Merkle proof for this leaf (off-chain)
bytes32[] memory merkleProof = new bytes32[](0); // Simplified
// Call claimSnowman with forged proof
SnowmanAirdrop(snowmanAirdrop).claimSnowman{value: 0}(
receiver,
merkleProof,
0, // v
0x0, // r
0x0 // s
);
}
}

Recommended Mitigation

- bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(receiver, amount))));
+ bytes32 leaf = keccak256(abi.encode(receiver, amount));

Explanation :
By exploiting the nested hashing (keccak256(bytes.concat(...))), an attacker could generate a (receiver, amount) pair that collides with a valid leaf hash in the Merkle tree. This allows them to bypass Merkle proof validation and claim tokens without eligibility.

Steps :

  1. Simplify Leaf Construction : Remove redundant hashing to ensure deterministic and unique leaf hashes for each (receiver, amount) pair.

  2. Validate Off-Chain Merkle Roots : Ensure Merkle roots are generated with consistent encoding rules to prevent mismatches.

Rationale :
Nested hashing introduces ambiguity in leaf generation, enabling collision attacks. Removing it ensures cryptographic integrity and prevents unauthorized claims.

Updates

Lead Judging Commences

yeahchibyke Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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