Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
Submission Details
Impact: high
Likelihood: high

Unrestricted Token Drain

Author Revealed upon completion

Root + Impact

Description

  • ​The faucet is expected to drip a fixed amount of ERC-20 ($RBT) per claim, enforcing a per-address cap and daily limit to ensure fair distribution.

// @> No check on cumulative amount claimed per address
_transfer(address(this), faucetClaimer, faucetDrip);

Risk

Likelihood:

  • Reason 1: Occurs every time the contract holds more than faucetDrip tokens and an attacker decides to exhaust the balance.

  • Reason 2: Trivial to automate with a script that generates 1,000 EOAs and calls the function in a loop.

Impact:

  • Impact 1: Complete denial-of-service for future legitimate users—faucet balance stays below faucetDrip and all subsequent claims revert with RaiseBoxFaucet_InsufficientContractBalance.

  • Impact 2: Reputation loss and potential depletion of marketing/community funds.

Proof of Concept

contract Attacker {
function drain(RaiseBoxFaucet faucet, uint256 times) external {
for (uint i = 0; i < times; ++i) {
address fresh = address(uint160(uint(keccak256(abi.encodePacked(i)))));
faucet.claimFaucetTokens{gas: 120000}(); // msg.sender == fresh
}
}
}

Recommended Mitigation

+ mapping(address => uint256) public totalClaimedPerAddress;
+ uint256 public constant MAX_CLAIM_PER_ADDRESS = 10_000e18;
function claimFaucetTokens() public {
+ require(totalClaimedPerAddress[msg.sender] + faucetDrip <= MAX_CLAIM_PER_ADDRESS,
+ "Claim cap exceeded");
+ totalClaimedPerAddress[msg.sender] += faucetDrip;
if (balanceOf(address(this)) <= faucetDrip) {
revert RaiseBoxFaucet_InsufficientContractBalance();
}
_transfer(address(this), faucetClaimer, faucetDrip);
}

Support

FAQs

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