claimFaucetTokens()Impact: Allows attackers to drain faucet ETH and tokens
Normal behavior:
claimFaucetTokens() should allow non-owner users to claim a fixed drip of faucet tokens every cooldown period, and (for first-time claimers) receive a small ETH drip.
It must enforce a per-user cooldown, respect daily claim limits, enforce a daily Sepolia ETH cap, reset daily counters on a day boundary, and follow the Checks-Effects-Interactions pattern to avoid reentrancy and state inconsistencies.
Issue (original contract):
The original implementation had several issues:
It used mutable public state and mutable configuration variables that were not protected by immutables, increasing attack surface.
Daily counters and day resets were handled inconsistently (some resets inside ETH drip branch, some after interactions), which could produce inaccurate daily counts.
It set a contract-level faucetClaimer state to msg.sender (unnecessary) and exposed mutable state.
There was no reentrancy guard around external ETH transfers followed by token transfers.
The ETH drip logic incorrectly resets dailyDrips to 0 in the else block whenever currentDay < lastDripDay, even if no new day has started.
This unnecessary reset breaks the intended daily tracking mechanism, leading to inaccurate counting of ETH drips and allowing more claims than the daily cap under certain conditions.
High: The problematic patterns execute on every call to claimFaucetTokens() by any user.
Medium: Owner-configurable variables combined with missing reentrancy protection and inconsistent counter resets create multiple realistic misuse or edge-case scenarios.
Loss or misallocation of Sepolia ETH (drips may occur incorrectly or be skipped unexpectedly).
Incorrect daily counters cause premature blocking of legitimate claims or allow over-drips of ETH.
Possible reentrancy vectors or state inconsistencies leading to fund loss or logic manipulation.
Confusing internal state (e.g., faucetClaimer) increases surface for mistaken assumptions during audits/integrations.
A reentrancy vulnerability exists in the claimFaucetTokens() function of the RaiseBoxFaucet contract.
A malicious contract can exploit this by re-entering through its receive() function when ETH is sent from the faucet, allowing multiple claims of both ETH and tokens in a single transaction.
The attached Foundry unit test demonstrates the exploit is practically reproducible against the original contract implementation.
src/RaiseBoxFaucet.sol — original vulnerable contract
test/ReentrancyTest.t.sol — Foundry test that executes the exploit
MaliciousClaimer — Attacker contract defined within the test
From the project root, execute the PoC test with Foundry:
The faucet lost 0.005 ETH during a single claim attempt (1.000 → 0.995 ETH).
The attacker gained +0.005 ETH, confirming a successful reentrant ETH drip.
Token Transfer and Claimed events were emitted multiple times inside the same transaction, indicating duplicate token distribution due to reentrancy.
reentryCount == 1 shows the attacker re-entered once from the faucet's ETH transfer flow.
The PoC confirms an actual fund-draining reentrancy in claimFaucetTokens().
By re-entering during the ETH transfer, an attacker can obtain additional ETH drips and duplicate token claims in a single transaction, causing measurable loss of faucet funds.
The patch below is minimal and focused: add ReentrancyGuard, remove faucetClaimer storage usage, and replace claimFaucetTokens() with a CEI-respecting nonReentrant implementation. Apply this diff to RaiseBoxFaucet.sol.
ReentrancyGuard: nonReentrant prevents reentry into claimFaucetTokens() (defense-in-depth).
CEI: Updating lastClaimTime, dailyClaimCount, hasClaimedEth, and dailyDrips before performing .call{value:...} removes the reentrancy window.
If the ETH transfer fails, the whole transaction reverts and no partial state persists.
Local claimer: Removing faucetClaimer storage from claimFaucetTokens() reduces unnecessary storage writes and avoids accidental reuse of a state variable as a per-call temporary.
For a more comprehensive view of other minor fixes and improvements, as well as detailed commit history, please refer to the repo: GitHub Link
Each fix is documented in the findings folder with corresponding .md files, allowing reviewers to track changes and understand all applied improvements.
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.