Root + Impact
Shared State Variable Creates Cross-Function DoS Attack Vector
Description
The contract suffers from a critical design flaw where a single timer variable (`s_earnTimer`) is shared between two distinct functions: `buySnow()` and `earnSnow()`. This creates a dangerous cross-contamination vulnerability where calling one function directly impacts the availability and timing constraints of the other function, leading to potential denial-of-service attacks and complete disruption of the contract's intended functionality.
The vulnerability exists because:
1. Both `buySnow()` and `earnSnow()` update the same `s_earnTimer` variable
2. The `earnSnow()` function checks if `block.timestamp < (s_earnTimer + 1 weeks) `before allowing execution
3. Any call to `buySnow()` resets this timer, potentially blocking `earnSnow()` for another week
4. This creates an unintended dependency between unrelated functions.
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);
}
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
@> s_earnTimer = block.timestamp;
}
Risk
Likelihood:
Zero technical barriers - Anyone with internet can execute this
Extremely low cost - ~$10-50/year to completely break the protocol
High incentive - Competitors, griefers, or anyone wanting to short the token
Accidental triggers - Even legitimate users unknowingly cause DoS by buying tokens
No countermeasures - Contract has zero protection against this attack
Timeline to exploitation:
This isn't a "might happen" - it's a "when will it happen" scenario. The vulnerability is too obvious, too cheap, and too devastating for attackers to ignore.
Impact:
CRITICAL SEVERITY - Complete Contract Dysfunction
This vulnerability can render the contract completely worthless through multiple attack vectors:
- An attacker can call `buySnow()` every 6 days and 23 hours
- This resets `s_earnTimer` just before the 1-week cooldown expires
- Result: `earnSnow()` becomes permanently unusable for all users
- Cost: Minimal (just the buy fee for 1 token every ~7 days)
**Griefing Attack Scenarios:**
- Attacker monitors mempool for earnSnow() transactions
- Front-runs with buySnow() calls to reset timer
- Legitimate users are consistently blocked from earning
- Creates frustration and abandonment of the protocol
Proof of Concept
run `forge test --match-test testDoSviaTimerContamination -vvv` with test in `TestSnow.t.sol`
Output:
```ngnix
[⠊] Compiling...
No files changed, compilation skipped
Ran 1 test for test/TestSnow.t.sol:TestSnow
[PASS] testDoSviaTimerContamination() (gas: 29571)
Logs:
=== ECONOMIC IMPACT ANALYSIS ===
Attack frequency: Every week
Cost per attack: 5000000000000000000 wei
Annual attack cost: 260000000000000000000 wei
Functions destroyed: earnSnow() (core protocol feature)
Users affected: ALL protocol users
Recovery method: NONE (requires contract redeployment)
COST-BENEFIT ANALYSIS:
- Attacker investment: Minimal ( 260000000000000000000 wei/year)
- Protocol damage: Total (earnSnow() permanently unusable)
- User impact: Complete loss of core functionality
- Token value impact: Severe degradation
function testDoSviaTimerContamination() public {
console2.log("ECONOMIC IMPACT ANALYSIS");
uint256 attacksPerYear = 52;
uint256 totalAttackCost = attacksPerYear * FEE;
console2.log("Attack frequency: Every week");
console2.log("Cost per attack:", FEE, "wei");
console2.log("Annual attack cost:", totalAttackCost, "wei");
console2.log("Functions destroyed: earnSnow() (core protocol feature)");
console2.log("Users affected: ALL protocol users");
console2.log("Recovery method: NONE (requires contract redeployment)");
console2.log("");
console2.log("COST-BENEFIT ANALYSIS:");
console2.log("- Attacker investment: Minimal (", totalAttackCost, "wei/year)");
console2.log("- Protocol damage: Total (earnSnow() permanently unusable)");
console2.log("- User impact: Complete loss of core functionality");
console2.log("- Token value impact: Severe degradation");
console2.log("");
console2.log("CONCLUSION: Extremely high impact, extremely low cost attack");
console2.log("Makes the contract unsuitable for production deployment");
}
Recommended Mitigation
This vulnerability can single-handedly destroy the protocol's functionality and value, making it unsuitable for production deployment without immediate remediation so i highly recommed Separate Timer Variables
uint256 private s_earnTimer; // Only for earnSnow()
uint256 private s_buyTimer; // Only for buySnow() if needed
function buySnow(uint256 amount) external payable canFarmSnow {
// ... rest of function logic
- s_earnTimer = block.timestamp;
+ s_buyTimer = block.timestamp;
// ... rest of function
}
function earnSnow() external canFarmSnow {
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp; // Only update earn timer
}