Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

[M-3] `claimTokenFaucets` is vulnerable to front-running attacks due to predictable state checks

[M-3] claimTokenFaucets is vulnerable to front-running attacks due to predictable state checks

Description

  • Expected bahaviour Token claims should be processed in a sequential, deterministic way (first-come first-serve basis). It should not be possible front-runners observing the mempool to bypass a submitted transaction when the daily ETH cap (dailySepEthCap) or daily count limit (dailyClaimCount) is about to be reached.

  • Problematic bahaviour Front-runners can monitor the state variables dailyDrips and dailyClaimCount. Once the claim limit is about to be exhausted (dailySepEthCap, dailyClaimCount) they can front-run an existing transaction in the mempool so that they receive the remaining ETH / tokens before the threshold is reached.

Root cause
The root cause of the front-running vulnerability is the predictability of the checks and the final state variable values in the claimFaucetTokens function:

function claimFaucetTokens() public {
.
.
.
@> if (balanceOf(address(this)) <= faucetDrip) {
revert RaiseBoxFaucet_InsufficientContractBalance();
}
@> if (dailyClaimCount >= dailyClaimLimit) {
revert RaiseBoxFaucet_DailyClaimLimitReached();
}
// still 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) {
hasClaimedEth[faucetClaimer] = true;
dailyDrips += sepEthAmountToDrip;
.
.
.
}

Risk

Likelihood: High

  • This exploit can happen on a daily basis when the faucet is about to reach the daily thresholds. It is a technically simple attack to carry out.

Impact: Medium

  • The front-running attacks will impact user experience at times when the daily thresholds are about to be reached.

  • Legitimate user transactions will be rejected (DoS).

  • Unfair advantage to sophisticated actors.

Proof of Concept

Given that Foundry processes transactions sequentially and a PoC will not be able to simulate a real front-running scenario, consider the following thought experiment:

  1. The daily faucet token claim limit is 100.

  2. 99 users successfully submit their token claims.

  3. The 100th user calls claimFaucetTokens and their transaction sits in the mempool.

  4. Front-runner sees this pending transaction and observes that there is only one remaining allowed token claim.

  5. They submits their own transaction with higher gas fees.

  6. Since validators prioritize higher gas fees, the front-runner's transaction is executed first.

  7. Front-runner receives tokens.

  8. The legitimate user's transaction is reverted with the error RaiseBoxFaucet_DailyClaimLimitReached (denied service).

Recommended Mitigation

A Request-based system with onlyOwner protection in processBatch method could be used to mitigate front-running attacks:

function requestTokens() external {
.
.
.
requests.push(...);
}
function processBatch(uint256 count) external onlyOwner {
// process requests in order
// not affected by gas manipulation
}

Other solutions include commit-reveal schemes or using a private mempool.

Updates

Lead Judging Commences

inallhonesty Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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