Expected behavior:
The claimFaucetTokens() function should follow the Checks–Effects–Interactions pattern strictly — meaning all internal state changes (like updating claim timestamps and counters) should occur before any external call (like sending ETH).
Actual behavior:
In the current implementation, the contract performs an external call to faucetClaimer.call{value: sepEthAmountToDrip} before updating critical state variables such as lastClaimTime and dailyClaimCount.
This introduces a reentrancy risk, because a malicious contract could re-enter claimFaucetTokens() through the ETH transfer fallback and claim multiple times before state updates are finalized.
Likelihood
1.Medium to High — The function performs a low-level call with value, which can be exploited by contracts with fallback logic.
2.Occurs naturally during normal claim flow when ETH is sent.
Impact
1.Double claim attack: A malicious contract could re-enter before cooldown and claim multiple times within a single transaction.
2.Cooldown bypass: Attackers could manipulate claim timing and daily limits.
3.Drained faucet: ETH or token balances could be exhausted due to recursive claims.
Explanation:
This PoC creates a malicious contract that re-enters claimFaucetTokens() upon receiving ETH.
Because the faucet only updates lastClaimTime after the ETH transfer, the reentrant call can occur multiple times, bypassing claim cooldowns and limits — effectively draining the faucet’s ETH or token reserves.
This ensures all bookkeeping happens before external calls.
Additionally, using nonReentrant (from OpenZeppelin’s ReentrancyGuard) adds another layer of protection against re-entry exploits.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.