In Solidity, uninitialized mapping entries return 0, which equals the first
enum member. The Status enum declares NICE as its first member (index 0).
Any address never touched by checkList or checkTwice automatically has
both s_theListCheckedOnce and s_theListCheckedTwice equal to
Status.NICE (the default value 0).
The collectPresent() function checks both mappings for Status.NICE at
line 154 — this check passes for every address that was never checked by
Santa, granting them unauthorized access to mint NFTs.
Likelihood:
Every address that has never interacted with Santa automatically qualifies
as NICE. Zero setup required — just call collectPresent() after the
Christmas timestamp.
The Christmas 2023 timestamp (1_703_480_381) has already passed, so this
is immediately exploitable on deployment to Arbitrum.
Impact:
Any address can mint an NFT without Santa's approval.
Unlimited addresses can collect, inflating NFT supply infinitely.
The entire naughty/nice checking mechanism is rendered completely
meaningless — the core protocol feature does not work.
A completely random address that was never checked by Santa on either list
calls collectPresent() after the Christmas timestamp. Both
s_theListCheckedOnce and s_theListCheckedTwice return their default
value (0 = NICE), so the status checks pass. The address receives an NFT
despite never being approved by Santa.
Reorder the enum so the default value (0) corresponds to a non-approved
status. By placing NOT_CHECKED_TWICE first, any uninitialized mapping
entry defaults to "not checked" instead of "nice", preventing unchecked
addresses from passing the status check in collectPresent().
## 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.