Raisebox Faucet

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

Block Timestamp Manipulation

Description

The claimFaucetTokens function relies on block.timestamp for the 3-day cooldown (CLAIM_COOLDOWN) and daily resets (lastFaucetDripDay, lastDripDay). Miners can manipulate block.timestamp within a small range (±15 seconds), potentially allowing users to claim slightly earlier than intended.

// Root cause in the codebase with @> marks to highlight the relevant section
function claimFaucetTokens() public {
// ... checks ...
@>if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
@> revert RaiseBoxFaucet_ClaimCooldownOn();
@>}
// ... daily reset logic ...
}

Risk

Likelihood:

  • Occurs when a miner manipulates block.timestamp to be slightly earlier than the actual time.

  • Occurs only within a small window (±15 seconds), limiting the frequency of exploitation.

Impact:

  • Users can claim tokens or ETH slightly before the 3-day cooldown or daily reset.

  • Minimal impact due to the long cooldown period and small manipulation range.

Proof of Concept

Explanation: The PoC demonstrates how a miner could set block.timestamp slightly early to allow a user to bypass the 3-day cooldown. By advancing time just before the cooldown period, the claim succeeds earlier than intended.

function testTimestampManipulation(RaiseBoxFaucet faucet) public {
vm.prank(user);
faucet.claimFaucetTokens();
vm.warp(block.timestamp + 3 days - 15 seconds); // Miner sets timestamp early
vm.prank(user);
faucet.claimFaucetTokens(); // Succeeds slightly before cooldown
}

Recommended Mitigation

Explanation: We replace block.timestamp with block numbers for the cooldown check, as block numbers are less susceptible to manipulation. We define constants for blocks per day and the cooldown period in blocks, updating the state to track lastClaimBlock instead of lastClaimTime.

+ uint256 public constant BLOCKS_PER_DAY = 5760; // Approx. 15s per block
+ uint256 public constant CLAIM_COOLDOWN_BLOCKS = 3 * BLOCKS_PER_DAY;
+ mapping(address => uint256) private lastClaimBlock;
function claimFaucetTokens() public {
- if (block.timestamp < (lastClaimTime[faucetClaimer] + CLAIM_COOLDOWN)) {
+ if (block.number < (lastClaimBlock[faucetClaimer] + CLAIM_COOLDOWN_BLOCKS)) {
revert RaiseBoxFaucet_ClaimCooldownOn();
}
- lastClaimTime[faucetClaimer] = block.timestamp;
+ lastClaimBlock[faucetClaimer] = block.number;
// ... rest of function ...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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