Description:
The encodeTokenId
and decodeTokenId
functions in FestivalPass.sol
are intended to pack a collectionId
and an itemId
into a single uint256
token ID and then reverse that operation. However, because there are no bounds checks on the sizes of collectionId
and itemId
, passing large values can cause bit overflow or overlap, leading to a mismatch between the original values and those returned by decodeTokenId
.
Impact:
Data Corruption & Enumeration Errors: Mismatched IDs will lead to incorrect metadata lookups and reporting; loops over token IDs may skip or duplicate entries.
Asset Misattribution: Users may end up viewing or transferring the wrong NFT, potentially breaking ownership assumptions.
Security Risks: Attackers could craft out-of-range IDs to collide with valid IDs, spoofing token metadata or manipulating balances in enumeration functions like getUserMemorabiliaDetailed
.
Proof of Concept:
Traces:
[13012] BeatTokenTest::testFuzz_EncodeDecodeEquals(2209565489602658888732503717266 [2.209e30], 8950265005652056424631159215985005930744246695058061687375512022564 [8.95e66])
├─ [0] VM::assume(true) [staticcall]
│ └─ ← [Return]
├─ [0] VM::assume(true) [staticcall]
│ └─ ← [Return]
├─ [1091] FestivalPass::encodeTokenId(2209565489602658888732503717266 [2.209e30], 8950265005652056424631159215985005930744246695058061687375512022564 [8.95e66]) [staticcall]
│ └─ ← [Return] 760826439674467069889829964273069317069602479457323566966187838221860 [7.608e68]
├─ [829] FestivalPass::decodeTokenId(760826439674467069889829964273069317069602479457323566966187838221860 [7.608e68]) [staticcall]
│ └─ ← [Return] 2235867954483924892482985086285 [2.235e30], 315535113242264627735032353991552740900 [3.155e38]
├─ [0] VM::assertEq(2235867954483924892482985086285 [2.235e30], 2209565489602658888732503717266 [2.209e30]) [staticcall]
│ └─ ← [Revert] assertion failed: 2235867954483924892482985086285 != 2209565489602658888732503717266
└─ ← [Revert] assertion failed: 2235867954483924892482985086285 != 2209565489602658888732503717266
Mitigation:
Enforce Input Bounds: Add require
statements in encodeTokenId
to ensure collectionId
and itemId
fit into their allocated bit-width (e.g., 64 bits each).
Use Safe Packing: Choose non-overlapping bit ranges (e.g., shift collectionId
by 128 bits) or use Solidity’s ABI encoding (abi.encodePacked
) followed by hashing if unique but inexact mapping is acceptable.
Document Limits: Clearly specify and enforce maximum collection and item counts to prevent overflow conditions.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.