Root + Impact
Zero Amount Bypass and Unlimited Minting Attack Vector
Description
The `buySnow()` function contains a critical input validation vulnerability that allows users to bypass payment requirements and potentially execute denial-of-service attacks. The function fails to validate that the `amount` parameter is greater than zero, creating multiple attack vectors:
1. Zero Amount Bypass: Users can call `buySnow(0)` with zero ETH to trigger timer manipulation without payment
2. Free Timer Reset: Attackers can reset `s_earnTimer` without purchasing any tokens
3. Gas-Efficient DoS: Enables low-cost denial-of-service attacks against `earnSnow()` functionality
4. Unlimited Function Calls: No rate limiting or amount restrictions allow spam attacks
The vulnerability stems from:
- Missing `require(amount > 0)` validation
- No maximum limit on the `amount` parameter
- Logic flaw allowing zero-value operations to succeed
- Lack of input sanitization before critical operations
function buySnow(uint256 amount) external payable canFarmSnow {
if (msg.value == (s_buyFee * amount)) {
_mint(msg.sender, amount);
} else {
i_weth.safeTransferFrom(msg.sender, address(this), (s_buyFee * amount));
_mint(msg.sender, amount);
}
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
Risk
Likelihood:
Anyone can call `buySnow(0)` repeatedly since there's no restriction on `amount > 0`.
The function still:
- Updates `s_earnTimer`
- Emits an event
- Consumes gas
- While it doesn't mint tokens or drain funds, it contaminates the earn timer, clutters logs, and wastes computation, which could:
- Block or grief legitimate users using earnSnow()
- Be spammed by bots
- Very cheap to execute. Easy to automate. Likely to be exploited by a malicious actor or even a curious user.
Impact:
This vulnerability creates several severe attack vectors:
1. Zero-Cost Timer Manipulation:
- Attackers can call `buySnow(0)` with `msg.value = 0` to reset `s_earnTimer`
- Bypasses the need to pay fees for timer contamination attacks
2. Enhanced Denial-of-Service:
- Combines with Timer Contamination vulnerability for more efficient attacks
- Attackers can spam `buySnow(0)` calls to maintain permanent DoS
- Significantly reduces the economic barrier to sustained attacks
Proof of Concept
run
forge `test --match-test testFreeMintingWithZeroAmount -vvv`
and `forge test --match-test testWETHPaymentBypassesETHPath -vvv` with test in `TestSnow.t.sol`
function testFreeMintingWithZeroAmount() public {
vm.startPrank(jerry);
weth.approve(address(snow), type(uint256).max);
uint256 balanceBefore = snow.balanceOf(jerry);
snow.buySnow(0);
uint256 balanceAfter = snow.balanceOf(jerry);
assertEq(balanceAfter - balanceBefore, 0);
vm.stopPrank();
}
function testWETHPaymentBypassesETHPath() public {
vm.startPrank(jerry);
weth.mint(jerry, FEE * 1_000);
weth.approve(address(snow), type(uint256).max);
uint256 snowBefore = snow.balanceOf(jerry);
snow.buySnow(1);
uint256 snowAfter = snow.balanceOf(jerry);
assertEq(snowAfter - snowBefore, 1);
vm.stopPrank();
}
Recommended Mitigation
While not directly stealing funds, this vulnerability significantly enhances the effectiveness of other attacks (particularly Timer Contamination) by reducing attack costs by over 90%. It transforms expensive attacks into nearly free operations, making protocol disruption accessible to any malicious actor with minimal resources
i so recommed Input Validation implementation
function buySnow(uint256 amount) external payable canFarmSnow {
+ if (amount == 0) {revert S__ZeroValue();}
// Rest of function logic...
}