Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Double Payment and ETH Theft Vulnerability in Snow::buySnow Function

Description:

The buySnow function contains a critical flaw in its payment handling logic. When users send ETH in an amount that doesn't exactly match s_buyFee * amount but have sufficient WETH balance/approval:

  • The contract falls back to using WETH for payment

  • The originally sent ETH remains trapped in the contract

  • User pays both the WETH fee AND loses their ETH
    This violates core security principles by enabling silent fund theft through normal protocol usage.

Impact:

  • Direct Financial Loss: Users permanently lose ETH sent to the contract

  • Double Payment: Users pay full price in WETH while losing ETH

  • Reputation Damage: Protocol appears to steal user funds

  • Permanent Fund Lock: Trapped ETH can only be recovered by privileged roles


Risk:

Likelihood:
• Common user error (incorrect ETH amount calculations)
• Wallet UIs may auto-populate gas + value confusion
• No frontend validation guarantees

Impact:
• Permanent ETH loss to victims
• No recovery mechanism for users
• Undermines trust in entire protocol

Proof of Concept

Test Scenario:

  1. Required fee for 100 tokens = 1 ETH

  2. User accidentally sends 0.5 ETH

  3. User has 1 WETH balance + approval

// Attack Sequence:
buySnow(100){value: 0.5 ether} // Calls vulnerable function
// Execution Path:
1. msg.value (0.5 ETH) != 1 ETH → enter else branch
2. WETH transfer succeeds (1 WETH deducted)
3. Tokens minted to user
4. 0.5 ETH remains locked in contract
Result:
  • User receives 100 Snow tokens

  • User loses 0.5 ETH permanently

  • User loses 1 WETH (converted to 1 ETH value)

  • Net loss: 1.5 ETH value for 100 tokens (50% overpayment)

##Recommended Mitigation

function buySnow(uint256 amount) external payable canFarmSnow {
+ uint256 totalFee = s_buyFee * amount;
- if (msg.value == (s_buyFee * amount)) {
- _mint(msg.sender, amount);
- } else {
+ if (msg.value > 0) {
+ // Strict ETH payment requirements
+ if (msg.value != totalFee) {
+ payable(msg.sender).transfer(msg.value);
+ revert S__IncorrectETHAmount();
+ }
+ _mint(msg.sender, amount);
+ } else {
i_weth.safeTransferFrom(msg.sender, address(this), totalFee);
_mint(msg.sender, amount);
}
emit SnowBought(msg.sender, amount);
}

Document Payment Rules:
"ETH payments must be exact - any over/under payment will be refunded and transaction reverted. WETH payments require exact token approval."

Updates

Lead Judging Commences

yeahchibyke Lead Judge
20 days ago
yeahchibyke Lead Judge 20 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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