AirDropper

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

M-5: Unrestricted account Parameter Allows Unintended Third-Party Claim Griefing

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

### Description
The internal logic within the `claim` execution structure exposes a structural flaw via an unvalidated target signature parameter configuration. The `claim(address account, ...)` entry sequence permits any arbitrary external caller (`msg.sender`) to supply any valid distribution recipient's identity parameters to finalize a claim execution sequence.
Because there are no strict transactional or cryptographic binding rules validating that the execution context matches the targeted recipient account parameters, a malicious actor or third-party entity can intercept public proofs and repeatedly invoke the protocol distribution execution path on behalf of users against their explicit wishes.
### Impact
* **Severity:** Medium
* **Likelihood:** Low-Medium
* **Consequence:** Vulnerable to griefing attack vectors and deep business requirement violations. If claiming requires users to manually execute auxiliary client actions, or if secondary fee components are introduced, third-party entities can force distribution claims early, creating unintended off-chain accounting side-effects.
### Proof of Concept
The following automated test case verifies that an unauthorized external address can invoke token distributions for a valid user address without possessing permissions or signatures.
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
contract AccountParamPoC is Test {
function testThirdPartyClaims() public {
AirdropToken token = new AirdropToken();
bytes32 root = 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd;
MerkleAirdrop airdrop = new MerkleAirdrop(root, token);
token.mint(address(this), 100e18);
token.transfer(address(airdrop), 100e18);
bytes32[] memory proof = new bytes32[](2);
proof[0] = 0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838;
proof[1] = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c;
address recipient = 0x20F41376c713072937eb02Be70ee1eD0D639966C;
address griefer = makeAddr("griefer");
vm.deal(griefer, 1 ether);
vm.startPrank(griefer);
// Griefer pays fee, recipient gets tokens arbitrarily
airdrop.claim{value: 1e9}(recipient, 25e18, proof);
vm.stopPrank();
assertEq(token.balanceOf(recipient), 25e18);
emit log("Griefer paid gas fee; third-party distribution forced successfully.");
}
}
```
### Recommended Mitigation
If gasless distribution or meta-transactions are not explicitly intended, enforce strict identity checks to ensure the transaction sender is the account claiming the tokens. Alternatively, incorporate an EIP-712 cryptographic signature validation step to ensure third parties claim tokens safely with explicit user permission.
```diff
function claim(address account, uint256 amount, bytes32[] calldata merkleProof) external payable {
+ if (account != msg.sender) revert MerkleAirdrop__NotYourClaim();
```
// Root cause in the codebase with @> marks to highlight the relevant section

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact 2

Proof of Concept

Recommended Mitigation

- remove this code
+ add this code
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour 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!