Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

M02. Pass Reconfiguration Without Initialization Check

Root + Impact

Description

The configurePass function is intended to initialize the price and maximum supply for predefined pass types (GENERAL_PASS, VIP_PASS, BACKSTAGE_PASS). It also unconditionally resets the pass’s current supply to zero, regardless of whether the pass was already in use.

This behavior introduces a risk: once any number of passes have been minted, calling configurePass again will reset the internal passSupply to zero, potentially enabling over-minting, violating the configured maxSupply, and breaking off-chain accounting assumptions.

The lack of initialization protection also makes it difficult to distinguish between an initial setup and a modification of an existing pass.

A better and safer design would be to split this into two clearly defined functions:

  • configurePass: For initial setup only.

  • updatePass: For future updates (price, max supply) without resetting current minted supply.

function configurePass(
uint256 passId,
uint256 price,
uint256 maxSupply
) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
@> passPrice[passId] = price;
@> passMaxSupply[passId] = maxSupply;
@> passSupply[passId] = 0; // ← Unsafe: resets supply unconditionally
}



Likelihood: Low

  • The issue will occur whenever the organizer calls configurePass on a pass that has already been configured and minted.

  • There are no checks in place to prevent repeated calls or warn that a pass is already in use.

Impact: Medium-Low

  • Over-minting becomes possible: if 50 passes have been minted, but passSupply is reset to 0, up to maxSupply more can be minted.

  • Off-chain systems that rely on accurate passSupply values may be misled.

  • User trust could degrade due to apparent inconsistencies or unexpected pass duplication.


Proof of Concept

Explanation: This PoC shows how calling configurePass again resets the internal supply counter, even though tokens have already been minted, allowing a total supply that exceeds the original cap.

// Step 1: Organizer configures VIP pass with 100 supply
festival.configurePass(VIP_PASS, 1 ether, 100);
// Step 2: 30 VIP passes are purchased
for (uint i = 0; i < 30; i++) {
festival.buyPass(VIP_PASS);
}
// passSupply[VIP_PASS] == 30
// Step 3: Organizer calls configurePass again with new price
festival.configurePass(VIP_PASS, 2 ether, 100);
// Now: passSupply[VIP_PASS] == 0 again, despite 30 already minted
// A user can now mint 100 more VIP passes, exceeding the limit

Mitigation

Explanation: The fix introduces a one-time-only setup with configurePass, and delegates future changes to updatePass, which validates that changes do not violate already minted supply. This prevents unintentional resets and encourages safer administrative operations.

Step 1: Make configurePass one-time-only

function configurePass(...) external onlyOrganizer {
+ require(passMaxSupply[passId] == 0, "Pass already configured");
...
- passSupply[passId] = 0;
}

Step 2: Add updatePass for controlled updates

function updatePass(uint256 passId, uint256 newPrice, uint256 newMaxSupply) external onlyOrganizer {
require(passMaxSupply[passId] > 0, "Pass not configured yet");
require(newPrice > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
passPrice[passId] = newPrice;
passMaxSupply[passId] = newMaxSupply;
emit PassUpdated(passId, newPrice, newMaxSupply);
}

Updates

Lead Judging Commences

inallhonesty Lead Judge 28 days ago
Submission Judgement Published
Validated
Assigned finding tags:

configurePass resets the current pass supply circumventing the max supply check

This is not acceptable as high because any attack vectors related to organizer trying to milk ETH from participants is voided by the fact that the organizer is trusted.

Support

FAQs

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