"Claim on behalf" is documented as a feature, but the function combines it with (a) a safeTransferFrom from receiver that requires a standing approval, (b) no deadline, and (c) the C-03 replay path. Once the signature exists, any party who obtains it can force the burn-and-mint at any moment.
This trades user timing control for "gasless claims" — a design choice that becomes unsafe given the other findings.
Likelihood:
Reason 1: Whenever a user broadcasts a claim tx that fails for any reason (gas, H-01 dust grief), the signature leaks to anyone watching the mempool.
Reason 2: Custodial relayers, frontends, and Telegram support channels routinely come into possession of pre-built signatures.
Impact:
Impact 1: A third party burns the user's Snow at an inconvenient moment, robbing the user of timing flexibility.
Impact 2: Combined with C-03 and M-04, the same signature replays indefinitely after the original claim.
The PoC simulates the realistic leak vector: Alice has signed her claim and pre-approved Snow to the airdrop (both are normal preconditions). A random relayer obtains the signature through any channel (mempool, frontend telemetry, a leaked support log). The relayer submits the claim using its own msg.sender, which the contract accepts because it never gates by msg.sender. After execution, Alice's Snow is burned and her Snowman is minted — at a moment Alice did not choose. The assert proves the claim went through against Alice's will; combined with C-03's replay path, the relayer can also choose to replay the claim later after Alice (or anyone) refills her balance.
The minimal fix adds a deadline parameter both as a function argument and as a field in the signed EIP-712 struct, so signatures bound to a deadline expire automatically. Users can also revoke a pending signature by calling a future invalidateSignature helper (not shown). If the "claim on behalf" feature is not core to the protocol, a stricter alternative is to require msg.sender == receiver, but that breaks the gasless-claim use case the README advertises — so a deadline is the safer middle ground.
The signed SnowmanClaim struct must also include deadline so its value is bound into the digest.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.