Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

Incorrect Token ID Encoding/Decoding: Bit-Packing Inversion Failure

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:

function testFuzz_EncodeDecodeEquals(uint256 collectionId, uint256 itemId) public view {
uint256 tokenId = fest.encodeTokenId(collectionId, itemId);
(uint256 decodedCollectionId, uint256 decodedItemId) = fest.decodeTokenId(tokenId);
assertEq(decodedCollectionId, collectionId);
assertEq(decodedItemId, itemId);
}

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.

Updates

Lead Judging Commences

inallhonesty Lead Judge
26 days ago
inallhonesty Lead Judge 25 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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