Beatland Festival

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

PassSupply reset Vulnerability


Description

The configurePass function is intended to allow the organizer to update the price and maximum supply for a pass type (e.g., GENERAL, VIP) without affecting the number of passes already sold. This ensures the contract can adapt to changing event needs while maintaining accurate supply tracking.

However, the function resets the current supply (passSupply[passId]) to zero every time it’s called. This erases the record of sold passes, allowing purchases to exceed the intended maximum supply after reconfiguration.

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; // Reset current supply
}

Risk

Likelihood:

This issue triggers whenever the organizer calls configurePass to adjust a pass type’s price or maximum supply.

It impacts any pass type with prior sales, as the supply counter resets regardless of how many passes were sold.

Impact:

Overselling beyond the maximum supply, leading to overbooking and potential event chaos.

Financial or reputational damage if the organizer cannot honor all sold passes.

Proof of Concept

Below is an additional test to add to your test suite. It builds on your existing test by pushing the supply to the limit, reconfiguring the pass, and demonstrating the overselling issue with clear logs and assertions.

function test_passSupplyAfterConfigure() public {
vm.prank(organizer);
festivalPass.configurePass(1, 0.1 ether, 100);
assertEq(festivalPass.passSupply(1), 0);
console.log("passSupply before passes sold:", festivalPass.passSupply(1));
// Users buy passes
vm.prank(user1);
festivalPass.buyPass{value: 0.1 ether}(1);
vm.prank(user2);
festivalPass.buyPass{value: 0.1 ether}(1);
assertEq(festivalPass.passSupply(1), 2);
console.log("passSupply after passes sold:", festivalPass.passSupply(1));
// Organizer reconfigures the pass
vm.prank(organizer);
festivalPass.configurePass(1, 0.2 ether, 100);
console.log("passSupply after the Price Configuration update:", festivalPass.passSupply(1));
// Logs show supply drops from 2 to 0
}

Recommended Mitigation

Remove the line that resets passSupply[passId] to preserve the current supply during reconfiguration.

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; // Remove this line
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months 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.