Raisebox Faucet

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

Vulnerability: Token Theft via Malicious Burn Function

Root + Impact

Description

  • The burnFaucetTokens function is designed to allow the owner to reduce the faucet's token supply by burning a specified amount of tokens from the contract's balance, maintaining the remaining tokens in the contract for future user claims.


  • The function incorrectly transfers the entire contract balance to the owner before burning only the specified amount, allowing the owner to permanently steal all remaining tokens while appearing to perform a legitimate burn operation.

function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
require(amountToBurn <= balanceOf(address(this)), "Faucet Token Balance: Insufficient");
// transfer faucet balance to owner first before burning
// ensures owner has a balance before _burn (owner only function) can be called successfully
_transfer(address(this), msg.sender, balanceOf(address(this))); // Transfers ALL tokens
_burn(msg.sender, amountToBurn); // Only burns specified amount
}

Risk

Likelihood:

  • The owner has unlimited access to call this function at any time with any burn amount, making exploitation trivial and always available.

  • The function design incentivizes malicious behavior since the owner receives (totalBalance - amountToBurn) tokens for free with every call, making it economically attractive to exploit.

Impact:

  • Complete theft of the faucet's token supply, allowing the owner to drain billions of tokens by calling the function with minimal burn amounts (e.g., burn 1 token, steal 999,999,999 tokens).

  • Total destruction of the faucet mechanism as the contract becomes empty, permanently breaking all future user claims and rendering the protocol non-functional.

Proof of Concept

The vulnerability can be demonstrated by showing how a malicious owner can drain the entire faucet supply while pretending to perform a small burn operation. The attack exploits the logical flaw where all tokens are transferred but only the specified amount is burned.

function testBurnTokenTheft() public {
// Initial state: Contract has 1 billion tokens, owner has 0
uint256 initialContractBalance = 1000000000 * 10**18;
uint256 initialOwnerBalance = 0;
assertEq(faucet.balanceOf(address(faucet)), initialContractBalance);
assertEq(faucet.balanceOf(owner), initialOwnerBalance);
// Owner pretends to burn only 1 token
uint256 amountToBurn = 1 * 10**18;
vm.prank(owner);
faucet.burnFaucetTokens(amountToBurn);
// Result: Owner steals 999,999,999 tokens
assertEq(faucet.balanceOf(address(faucet)), 0); // Contract emptied
assertEq(faucet.balanceOf(owner), initialContractBalance - amountToBurn); // Owner gets the rest
// Faucet is now broken - users cannot claim
vm.prank(user1);
vm.expectRevert(); // RaiseBoxFaucet_InsufficientContractBalance
faucet.claimFaucetTokens();
}

Recommended Mitigation

The fix involves changing the transfer amount from the entire contract balance to only the amount intended to be burned. This ensures that only the tokens meant for burning are moved to the owner's address, preventing theft of the remaining supply.

- _transfer(address(this), msg.sender, balanceOf(address(this)));
+ _transfer(address(this), msg.sender, amountToBurn);
Updates

Lead Judging Commences

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

Unnecessary and convoluted logic in burnFaucetTokens

Support

FAQs

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