AirDropper

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

Potential Reentrancy in claimFees Function

Root + Impact

Description

  • The `claimFees()` function uses a low-level `call` to send ETH to the owner. While there are no state changes after the call, this violates the Checks-Effects-Interactions pattern and could theoretically be exploited if the owner is a malicious contract.

    ### Root + Impact

    The function uses `call` for ETH transfer without following CEI pattern, though no state changes occur after the call.

    ```solidity

    // src/MerkleAirdrop.sol:42-47

    function claimFees() external onlyOwner {

    (bool succ,) = payable(owner()).call{ value: address(this).balance }("");

    if (!succ) {

    revert MerkleAirdrop__TransferFailed();

    }

    }

    ```

    If the owner is a contract with a malicious fallback/receive function, it could potentially reenter, though there are no state changes to manipulate. This is a low-risk issue but violates security best practices.


Risk

Likelihood:

  • * Owner would need to be a malicious contract

    * The malicious contract would need to reenter during the call

    * No state changes occur after the call, limiting attack surface

    * This is unlikely but possible

Impact:

  • * If owner is malicious, could potentially cause unexpected behavior during reentrancy

    * Violates security best practices

    * Could cause issues if contract is upgraded or modified in future

    * Low impact due to no post-call state changes

Proof of Concept

1. Owner is set to a malicious contract
2. Owner calls `claimFees()`
3. ETH is sent via `call` to owner's contract
4. Owner's receive/fallback function is called
5. Owner could theoretically reenter, though no state exists to manipulate

Recommended Mitigation

```diff
// src/MerkleAirdrop.sol:42-47
function claimFees() external onlyOwner {
+ uint256 balance = address(this).balance;
+ if (balance == 0) {
+ revert MerkleAirdrop__NoFeesToClaim();
+ }
- (bool succ,) = payable(owner()).call{ value: address(this).balance }("");
+ (bool succ,) = payable(owner()).call{ value: balance }("");
if (!succ) {
revert MerkleAirdrop__TransferFailed();
}
}
```
Or use OpenZeppelin's Address library:
```diff
+ import { Address } from "@openzeppelin/contracts/utils/Address.sol";
// ...
function claimFees() external onlyOwner {
uint256 balance = address(this).balance;
- (bool succ,) = payable(owner()).call{ value: address(this).balance }("");
- if (!succ) {
- revert MerkleAirdrop__TransferFailed();
- }
+ Address.sendValue(payable(owner()), balance);
}
```
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 16 days 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!