Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Valid

In `Snow::buySnow` function `s_earnTimer` is set, allowing attackers to Make DoS

In Snow::buySnow function s_earnTimer is set, allowing attackers to Make DoS

Description

In Snow contract users are allowed to buy or earn a token with in a week. but setting s_earnTimer in Snow::buySnow will allow attackers to send some to little values to block other people from earning free token once a week. In addition there is no checks for input amount in Snow::buySnow so attacker could cause deniel of service without sending anything.

function buySnow(uint256 amount) external payable canFarmSnow {
// @audit-low no checks for msg.value < buyfee*amount
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);
}
// q why we hit timer for only one msg.sender in one global variable
@> s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
function earnSnow() external canFarmSnow {
// @audit-high attacker call buy with zero to prevent this
@> if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}

Impact: Deniel of Service

Proof of Concept

Add this test suit to TestSnow.t.sol

In this test function victory is the attacker
victory buy zero amount of token so no one can't earn anything until after a week
and he keep doing the same thing every week the moment 1 week passed

function buySnow(uint256 amount) external payable canFarmSnow {
// @audit-low no checks for msg.value < buyfee*amount
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);
}
// q why we hit timer for only one msg.sender in one global variable
s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}
function earnSnow() external canFarmSnow {
// @audit-high attacker call buy with zero to prevent this
if (s_earnTimer != 0 && block.timestamp < (s_earnTimer + 1 weeks)) {
revert S__Timer();
}
_mint(msg.sender, 1);
s_earnTimer = block.timestamp;
}

Recommended Mitigation

One could say since any one can buy any amount of token not setting s_earnTimer in Snow::buySnow can avoid this

function buySnow(uint256 amount) external payable canFarmSnow {
// @audit-low no checks for msg.value < buyfee*amount
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);
}
// q why we hit timer for only one msg.sender in one global variable
- s_earnTimer = block.timestamp;
emit SnowBought(msg.sender, amount);
}

Or you could make a d/t variable for buySnow like s_buyTimer and limit how much a person can mint to control inflation

Updates

Lead Judging Commences

yeahchibyke Lead Judge 11 days ago
Submission Judgement Published
Validated
Assigned finding tags:

buying of snow resets global timer thus affecting earning of free snow

When buySnow is successfully called, the global timer is reset. This inadvertently affects the earning of snow as that particular action also depends on the global timer.

Support

FAQs

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