Only addresses that Santa has explicitly marked as NICE on both s_theListCheckedOnce and s_theListCheckedTwice should be eligible to mint a Christmas NFT via collectPresent(). The intent of the protocol is that an unchecked address has no claim on any present.
The Status enum declares NICE as the first variant. In Solidity, the default value of any uninitialized mapping entry of an enum type is the first variant — i.e. Status.NICE (uint8(0)). Because s_theListCheckedOnce and s_theListCheckedTwice both return Status.NICE for every never-touched address, the collectPresent NICE branch evaluates to true for every address Santa has never seen. Combined with the fact that CHRISTMAS_2023_BLOCK_TIME = 1_703_480_381 (Dec 25 2023) is already in the past, any address on Arbitrum can call collectPresent() once and mint a free NFT with no involvement from Santa.
Likelihood: High — every address on the chain meets the precondition with zero setup. CHRISTMAS_2023_BLOCK_TIME is already in the past on every live deployment, so the only gate (Santa's two-step check) is silently auto-passed by the enum default.
Impact: High — the entire access-control model of the NFT mint is bypassed. Santa's checkList / checkTwice calls become decorative. NFT supply is uncapped (every EOA can mint one), and any future logic that gates rewards on collectPresent having been called by genuinely-nice users is broken.
Place this test in test/SantasListTest.t.sol (alongside the existing test file) and run with forge test --mt test_AnyoneCanCollectFreeNFT -vvv. The exploit runs against a fresh deployment with no checkList / checkTwice calls — the attacker is a completely random address that Santa has never seen.
Run output (expected):
The exploit can be repeated from every fresh EOA, minting an unbounded number of NFTs to the same human attacker.
Reorder the enum so the zero / default variant represents the un-checked state, and update collectPresent to require explicit NICE markings:
Optionally, harden collectPresent with an explicit guard so any future re-ordering of the enum cannot reintroduce the bug:
## Description `collectPresent` function is supposed to be called by users that are considered `NICE` or `EXTRA_NICE` by Santa. This means Santa is supposed to call `checkList` function to assigned a user to a status, and then call `checkTwice` function to execute a double check of the status. Currently, the enum `Status` assigns its default value (0) to `NICE`. This means that both mappings `s_theListCheckedOnce` and `s_theListCheckedTwice` consider every existent address as `NICE`. In other words, all users are by default double checked as `NICE`, and therefore eligible to call `collectPresent` function. ## Vulnerability Details The vulnerability arises due to the order of elements in the enum. If the first value is `NICE`, this means the enum value for each key in both mappings will be `NICE`, as it corresponds to `0` value. ## Impact The impact of this vulnerability is HIGH as it results in a flawed mechanism of the present distribution. Any unchecked address is currently able to call `collectPresent` function and mint an NFT. This is because this contract considers by default every address with a `NICE` status (or 0 value). ## Proof of Concept The following Foundry test will show that any user is able to call `collectPresent` function after `CHRISTMAS_2023_BLOCK_TIME` : ``` function testCollectPresentIsFlawed() external { // prank an attacker's address vm.startPrank(makeAddr("attacker")); // set block.timestamp to CHRISTMAS_2023_BLOCK_TIME vm.warp(1_703_480_381); // collect present without any check from Santa santasList.collectPresent(); vm.stopPrank(); } ``` ## Recommendations I suggest to modify `Status` enum, and use `UNKNOWN` status as the first one. This way, all users will default to `UNKNOWN` status, preventing the successful call to `collectPresent` before any check form Santa: ``` enum Status { UNKNOWN, NICE, EXTRA_NICE, NAUGHTY } ``` After modifying the enum, you can run the following test and see that `collectPresent` call will revert if Santa didn't check the address and assigned its status to `NICE` or `EXTRA_NICE` : ``` function testCollectPresentIsFlawed() external { // prank an attacker's address vm.startPrank(makeAddr("attacker")); // set block.timestamp to CHRISTMAS_2023_BLOCK_TIME vm.warp(1_703_480_381); // collect present without any check from Santa vm.expectRevert(SantasList.SantasList__NotNice.selector); santasList.collectPresent(); vm.stopPrank(); } ```
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.