Vyper Vested Claims

First Flight #34
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: medium
Invalid

Merkle Tree Implementation Vulnerability

Summary

The token vesting contract contains a vulnerability in its Merkle tree proof verification logic. The _verify_proof function does not properly implement the standard Merkle tree verification algorithm as it lacks position information for each proof element. While the _hash_pair function attempts to handle ordering by sorting the inputs before hashing, this implementation does not align with standard Merkle tree verification practices.

Vulnerability Details

In the current implementation:

def _verify_proof(proof: DynArray[bytes32, 20], leaf: bytes32) -> bool:
"""
@notice This function is used to verify the merkle proof
@param proof: DynArray[bytes32, 20], the merkle proof
@param leaf: bytes32, the leaf node
@return bool: True if the proof is valid
"""
computed_hash: bytes32 = leaf
for proof_element: bytes32 in proof:
computed_hash = self._hash_pair(computed_hash, proof_element)
return computed_hash == self.merkle_root

https://github.com/CodeHawks-Contests/2025-02-vyper-vested-claims/blob/423abfceab774ff05ad2a851c9f7830e7cb36ac6/src/VestedAirdrop.vy#L61-L71

The function attempts to handle the ordering using:

@pure
def _hash_pair(a: bytes32, b: bytes32) -> bytes32:
"""
@notice This function is used to hash a pair of bytes32
@param a: bytes32
@param b: bytes32
@return bytes32: the hash of the pair
"""
if convert(a, uint256) < convert(b, uint256):
return keccak256(concat(a, b))
return keccak256(concat(b, a))

https://github.com/CodeHawks-Contests/2025-02-vyper-vested-claims/blob/423abfceab774ff05ad2a851c9f7830e7cb36ac6/src/VestedAirdrop.vy#L50-L59

Impact

This implementation could lead to incorrect proof validations in the following scenarios:

  1. False Positives: In certain tree structures, a proof might be incorrectly validated as true when it should be false.

  2. False Negatives: Valid proofs might be rejected if the Merkle tree generator used a different hashing order convention.

  3. Incompatibility: The contract might not work with standard Merkle tree libraries that include position information (left/right indicators).

This could potentially allow unauthorized claims if an attacker crafts a specific proof that exploits the sorting logic, or it could prevent legitimate users from claiming their tokens if the proof generation system doesn't match the contract's verification approach.

Tools Used

Manual Review

Recommendations

Implement a standard Merkle proof verification that includes position information:

def _verify_proof(proof: DynArray[bytes32, 20], positions: DynArray[bool, 20], leaf: bytes32) -> bool:
"""
@notice This function is used to verify the merkle proof
@param proof: DynArray[bytes32, 20], the merkle proof
@param positions: DynArray[bool, 20], positions indicating whether proof element is left (True) or right (False)
@param leaf: bytes32, the leaf node
@return bool: True if the proof is valid
"""
computed_hash: bytes32 = leaf
for i in range(len(proof)):
proof_element: bytes32 = proof[i]
if positions[i]: # If true, proof element is on the left
computed_hash = keccak256(concat(proof_element, computed_hash))
else: # If false, proof element is on the right
computed_hash = keccak256(concat(computed_hash, proof_element))
return computed_hash == self.merkle_root
Updates

Appeal created

bube Lead Judge 6 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.