Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Incorrect dailyDrips Reset Logic bypass daily sepEth cap

Root + Impact

Description

  • The claimFaucetTokens() logic resets dailyDrips when currentDay > lastDripDay, but also resets it unconditionally for non-first-time claimers

  • Once any non-first-time claimers claims, it will reset accumulated daily drips, even it hasn't been a day

function claimFaucetTokens() public {
// Checks
...
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
uint256 currentDay = block.timestamp / 24 hours;
if (currentDay > lastDripDay) {
lastDripDay = currentDay;
dailyDrips = 0;
// dailyClaimCount = 0;
}
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
...
} else {
...
}
} else {
@> // reset dailyDrips to 0 whenever a non-first-time claimer claims
dailyDrips = 0;
}
...
}

Risk

Likelihood: High

  • The branch is reachable by any address that has claimed before

Impact:

  • Cap bypass: dailySepEthCap can be exceeded repeatedly within the same day by alternating first-time claims (which add to dailyDrips) and any repeat claim (which resets it)

  • Fund drain: accelerated ETH outflow beyond intended daily limits

Proof of Concept

Add the following test, then run this command: forge test --match-test testResetDailyETHCap -vv

function testResetDailyETHCap() public {
console.log("initial daily drips:", raiseBoxFaucet.dailyDrips());
vm.prank(user1);
raiseBoxFaucet.claimFaucetTokens();
console.log("daily drips after user1 first claim:", raiseBoxFaucet.dailyDrips());
advanceBlockTime(block.timestamp + 3 days);
console.log("time advanced by 3 days");
vm.prank(user2);
raiseBoxFaucet.claimFaucetTokens();
console.log("daily drips after user2 first claim:", raiseBoxFaucet.dailyDrips());
vm.prank(user3);
raiseBoxFaucet.claimFaucetTokens();
console.log("daily drips after user3 first claim:", raiseBoxFaucet.dailyDrips());
vm.prank(user1);
raiseBoxFaucet.claimFaucetTokens();
console.log("daily drips after user1 second claim:", raiseBoxFaucet.dailyDrips());
}

PoC Results:

[⠊] Compiling...
No files changed, compilation skipped
Ran 1 test for test/RaiseBoxFaucet.t.sol:TestRaiseBoxFaucet
[PASS] testResetDailyETHCap() (gas: 480307)
Logs:
initial daily drips: 0
daily drips after user1 first claim: 5000000000000000
time advanced by 3 days
daily drips after user2 first claim: 5000000000000000
daily drips after user3 first claim: 10000000000000000
daily drips after user1 second claim: 0
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.73ms (1.40ms CPU time)
Ran 1 test suite in 327.42ms (7.73ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

  • Remove the unconditional dailyDrips = 0 line.

  • Ensure resets only when the timestamp-based rollover condition is met

function claimFaucetTokens() public {
// Checks
...
if (!hasClaimedEth[faucetClaimer] && !sepEthDripsPaused) {
uint256 currentDay = block.timestamp / 24 hours;
if (currentDay > lastDripDay) {
lastDripDay = currentDay;
dailyDrips = 0;
// dailyClaimCount = 0;
}
if (dailyDrips + sepEthAmountToDrip <= dailySepEthCap && address(this).balance >= sepEthAmountToDrip) {
...
} else {
...
}
+ }
- } else {
- dailyDrips = 0;
- }
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 9 days ago
Submission Judgement Published
Validated
Assigned finding tags:

dailyDrips Reset Bug

Support

FAQs

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