Snowman Merkle Airdrop

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

[H-3] CEI Violation and Reentrancy Vulnerability in earnSnow

Root + Impact


  • Root: The earnSnow function performs an external call (_mint) before updating s_earnTimer, violating CEI, and lacks reentrancy protection;

  • Impact: An attacker can re-enter the function multiple times, earning free Snow tokens beyond the once-a-week limit, potentially exhausting the supply.

Description

  • The earnSnow function checks the weekly timer but calls _mint(msg.sender, 1) before setting s_earnTimer = block.timestamp. This allows a reentrancy attack where a malicious contract re-calls earnSnow before the state update, bypassing the s_earnTimer check.

// Root cause in the codebase with @> marks to highlight the relevant section// Root cause in the codebase with @> marks to highlight the relevant section
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:

  • When an attacker deploys a malicious contract that receives the minted token and re-enters earnSnow via a callback (e.g., receive or onERC721Received).

  • During the farming period with untrusted token interactions.

Impact:

  • Allows unlimited free token minting, undermining the once-a-week limit.

  • Potential depletion of the total supply or economic imbalance.

Proof of Concept

  • Reentrancy Attack Steps:

  1. An attacker deploys a malicious contract with a receive function that calls earnSnow again.

  2. The attacker calls earnSnow on the Snow contract, triggering _mint(msg.sender, 1) to mint 1 token to the attacker’s contract.

  3. The malicious contract’s receive function executes during the _mint callback, re-calling earnSnow before s_earnTimer is updated.

  4. The second call passes the timer check (since s_earnTimer is still 0 or unchanged), minting another token.

  5. This process can repeat (limited by gas or contract logic), allowing multiple free tokens in one week.

Recommended Mitigation

import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Snow is ERC20, Ownable, ReentrancyGuard {
function earnSnow() external canFarmSnow,
+ nonReentrant{
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
- _mint(msg.sender, 1);
- s_earnTimer = block.timestamp;
+ s_earnTimer = block.timestamp; // State update
+ _mint(msg.sender, 1); // External call
}
}
  • Add nonReentrant to block reentrancy and reorder CEI.

Updates

Lead Judging Commences

yeahchibyke Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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