AirDropper

AI First Flight #5
Beginner FriendlyDeFiFoundry
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Users can claim on behalf of others leading to griefing and loss of claim control

Root + Impact

Description

  • Normal Behavior

    The claim() function is expected to allow a user to claim their own airdrop allocation securely and independently.

  • Issue

    The contract allows any caller to submit a claim for any account, as long as they provide a valid Merkle proof.

  • There is no validation that:

    msg.sender == account

    This allows third parties to trigger claims on behalf of others.

function claim(address account, uint256 amount, bytes32[] calldata merkleProof) external payable {
// @> No validation that msg.sender is the account
bytes32 leaf = keccak256(abi.encodePacked(account, amount));
if (!MerkleProof.verify(merkleProof, i_merkleRoot, leaf)) {
revert MerkleAirdrop__InvalidProof();
}
i_airdropToken.safeTransfer(account, amount);
}

Risk

Likelihood:

  • Anyone with access to a valid Merkle proof can call claim()

  • No restriction ensures msg.sender matches the claimant

Impact:

  • Attackers can front-run or force claims for other users

Users lose control over when and how they claim their airdrop

  • Can lead to unexpected fee payments or disrupted UX

Proof of Concept

  • The attacker uses a valid proof belonging to another user

  • Calls claim() using victim’s address

  • Contract verifies proof and sends tokens to victim

  • Victim has no control over when the claim happens

function test_ClaimOnBehalfOfAnotherUser() public {
address victim = address(0x123);
address attacker = address(0x999);
vm.deal(attacker, 1 ether);
// Assume valid proof for victim
bytes32[] memory proof = getValidProof(victim);
vm.startPrank(attacker);
// Attacker triggers claim for victim
airdrop.claim{value: 1e9}(victim, 25, proof);
vm.stopPrank();
// Victim receives tokens without initiating claim
assert(token.balanceOf(victim) == 25);
}

Recommended Mitigation

  • Adds a check ensuring only the intended recipient can call claim()

  • Prevents third-party interference

  • Restores user control over claiming process

function claim(address account, uint256 amount, bytes32[] calldata merkleProof) external payable {
+ // Ensure only the rightful user can claim
+ require(msg.sender == account, "Not authorized");
if (msg.value != FEE) {
revert MerkleAirdrop__InvalidFeeAmount();
}
bytes32 leaf = keccak256(abi.encodePacked(account, amount));
if (!MerkleProof.verify(merkleProof, i_merkleRoot, leaf)) {
revert MerkleAirdrop__InvalidProof();
}
i_airdropToken.safeTransfer(account, amount);
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!