Snowman Merkle Airdrop

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

[H-02] Type-Hash DoS

Type-Hash DoS

Description

  • Normal behaviour: Off-chain wallets generate an EIP-712 signature using the struct string "address receiver".

  • Issue: The contract misspells the string ("addres receiver"), so any correctly produced signature is rejected.

@> bytes32 private constant MESSAGE_TYPEHASH =
@> keccak256("SnowmanClaim(addres receiver, uint256 amount)");
// ^ typo: “addres”

Risk

Likelihood

  • Occurs whenever a standard EIP-712 library is used.

  • Every user signing through MetaMask, Ethers.js, Viem, etc., will hit it.

Impact

  • Users cannot claim; airdrop effectively bricked until code is patched.

  • Support costs and user frustration.

Proof of Concept

test/PoC_SnowmanAirdrop_TypedHashDoS.t.sol

// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {Snow} from "../src/Snow.sol";
import {Snowman} from "../src/Snowman.sol";
import {SnowmanAirdrop} from "../src/SnowmanAirdrop.sol";
import {MockWETH} from "../src/mock/MockWETH.sol";
import {Helper} from "../script/Helper.s.sol";
/**
* @title PoC_TypedHash_DoS
* @notice Shows that any wallet / SDK that follows the **correct** EIP-712
* typed-data string ("address receiver") will generate a signature that
* the contract rejects, due to the typo ("addres receiver") baked into
* `MESSAGE_TYPEHASH`.
*/
contract PoC_SnowmanAirdrop_TypedHashDoS is Test {
Snow snow;
Snowman nft;
SnowmanAirdrop airdrop;
MockWETH weth;
Helper deployer;
// Alice's Merkle proof (same as used in canonical tests)
bytes32[] AL_PROOF;
address alice;
uint256 alKey;
function setUp() public {
deployer = new Helper();
(airdrop, snow, nft, weth) = deployer.run();
(alice, alKey) = makeAddrAndKey("alice");
vm.prank(alice);
snow.approve(address(airdrop), type(uint256).max);
// populate proof
AL_PROOF = new bytes32[](3);
AL_PROOF[0] = 0xf99782cec890699d4947528f9884acaca174602bb028a66d0870534acf241c52;
AL_PROOF[1] = 0xbc5a8a0aad4a65155abf53bb707aa6d66b11b220ecb672f7832c05613dba82af;
AL_PROOF[2] = 0x971653456742d62534a5d7594745c292dda6a75c69c43a6a6249523f26e0cac1;
}
function _domainSeparator() internal view returns (bytes32) {
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("Snowman Airdrop")),
keccak256(bytes("1")),
block.chainid,
address(airdrop)
)
);
}
function testClaimFailsWithCorrectTypehash() public {
uint256 amount = snow.balanceOf(alice);
// struct hash using the *correct* type string
bytes32 structHash = keccak256(
abi.encode(
keccak256("SnowmanClaim(address receiver, uint256 amount)"),
alice,
amount
)
);
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), structHash));
(uint8 v, bytes32 r, bytes32 s) = vm.sign(alKey, digest);
vm.expectRevert(SnowmanAirdrop.SA__InvalidSignature.selector);
vm.prank(alice);
airdrop.claimSnowman(alice, AL_PROOF, v, r, s);
}
}

Recommended Mitigation

- "SnowmanClaim(addres receiver, uint256 amount)"
+ "SnowmanClaim(address receiver, uint256 amount)"

Updates

Lead Judging Commences

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

Inconsistent MESSAGE_TYPEHASH with standard EIP-712 declaration

A typo in the `MESSAGE_TYPEHASH` variable of the `SnowmanAirdrop` contract will prevent signature verification claims. Used `addres` instead of `address`

Support

FAQs

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