Santa's List

AI First Flight #3
Beginner FriendlyFoundry
EXP
View results
Submission Details
Severity: high
Valid

Unchecked Enum Of User Address Can Still Collect The NFT

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

//Normal behavior: Santa memanggil checkList() lalu checkTwice() untuk menandai status seseorang sebagai NICE atau EXTRA_NICE sebelum orang tersebut berhak memanggil collectPresent() dan menerima NFT (dan SantaToken untuk EXTRA_NICE).
//Masalahnya: enum Status { NICE, EXTRA_NICE, NAUGHTY, NOT_CHECKED_TWICE } menempatkan NICE di index 0. Di Solidity, setiap entry mapping yang belum pernah di-assign otomatis bernilai zero value dari tipe datanya — untuk enum, itu berarti index 0. Akibatnya, s_theListCheckedOnce[address] dan s_theListCheckedTwice[address] untuk alamat mana pun yang belum pernah disentuh Santa sama sekali akan otomatis terbaca sebagai Status.NICE
​
enum Status {
@> NICE, // index 0 -> default value untuk semua address yang belum pernah di-checkList/checkTwice
EXTRA_NICE,
NAUGHTY,
NOT_CHECKED_TWICE
}
​
​
function collectPresent() external {
if (block.timestamp < CHRISTMAS_2023_BLOCK_TIME) {
revert SantasList__NotChristmasYet();
}
if (balanceOf(msg.sender) > 0) {
revert SantasList__AlreadyCollected();
}
@> if (s_theListCheckedOnce[msg.sender] == Status.NICE && s_theListCheckedTwice[msg.sender] == Status.NICE) {
_mintAndIncrement();
return;
}
...
}
​
//RISK
//Terjadi setiap kali collectPresent() dipanggil oleh alamat yang belum pernah diperiksa Santa sama sekali (checkList/checkTwice belum pernah dipanggil untuknya) — Santa tidak perlu melakukan kesalahan apa pun; ini adalah state default dari sistem, bukan kondisi tepi yang jarang terjadi.
//Berlaku untuk setiap alamat di chain setelah CHRISTMAS_2023_BLOCK_TIME terlewati, bukan hanya satu alamat spesifik.
​
//IMPACT
//Seluruh mekanisme naughty-or-nice yang dikurasi Santa dilewati sepenuhnya — alamat yang tidak pernah dinilai justru diperlakukan sebagai NICE secara default.
//Setiap alamat mint NFT (_mintAndIncrement()) tanpa hak yang sah, sehingga suplai NFT dan s_tokenCounter menggelembung tak terbatas dan merusak kelangkaan/nilai dari koleksi tersebut.

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact 2

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
​
import {Test} from "forge-std/Test.sol";
import {SantasList} from "../src/SantasList.sol";
​
contract UncheckedDefaultNicePoCTest is Test {
SantasList santasList;
address santa = makeAddr("santa");
address stranger = makeAddr("stranger"); // never touched by Santa at all
​
function setUp() public {
vm.prank(santa);
santasList = new SantasList();
}
​
function test_NeverCheckedAddressCanStillCollectPresent() public {
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
​
assertEq(santasList.balanceOf(stranger), 0);
​
// stranger was NEVER checked by Santa via checkList()/checkTwice()
vm.prank(stranger);
santasList.collectPresent();
​
assertEq(santasList.balanceOf(stranger), 1); // present collected anyway
}
}

Recommended Mitigation

enum Status {
- NICE,
- EXTRA_NICE,
- NAUGHTY,
- NOT_CHECKED_TWICE
+ NOT_CHECKED_TWICE,
+ NICE,
+ EXTRA_NICE,
+ NAUGHTY
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-02] All addresses are considered `NICE` by default and are able to claim a NFT through `collectPresent` function before any Santa check.

## 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(); } ```

Support

FAQs

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

Give us feedback!