Beatland Festival

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

Organizer Role Transfer Without Safeguards

Root + Impact

The setOrganizer function lacks proper safeguards and can transfer critical permissions to invalid addresses, potentially locking out legitimate organizers or enabling malicious takeovers.

Description

  • The normal behavior should include safeguards like two-step transfers or timelock mechanisms for critical role changes

  • The current implementation allows immediate, irreversible organizer role transfer without validation or confirmation mechanisms

function setOrganizer(address _organizer) public onlyOwner {
@> organizer = _organizer; // Immediate transfer, no validation
@> // No event emission
@> // No confirmation mechanism
@> // No timelock or delay
}
modifier onlyOrganizer() {
@> require(msg.sender == organizer, "Only organizer can call this"); // Single point of failure
_;
}

Risk

Likelihood:

  • Human error in address input during organizer changes

  • Potential for owner to be compromised and maliciously change organizer

Impact:

  • Complete loss of organizer functions if wrong address is set

  • Malicious new organizer could manipulate all festival parameters

  • No way to recover from incorrect organizer assignment

  • Festival operations could be permanently halted

Proof of Concept

function testOrganizerLockout() public {
// Owner accidentally sets wrong organizer address
vm.startPrank(owner);
address wrongAddress = address(0xdead); // Typo or wrong address
festival.setOrganizer(wrongAddress);
vm.stopPrank();
// Original organizer can no longer perform functions
vm.startPrank(organizer);
vm.expectRevert("Only organizer can call this");
festival.createPerformance(block.timestamp + 1 hours, 1 hours, 100 ether);
vm.stopPrank();
// Wrong address cannot perform functions either (no private key)
vm.startPrank(wrongAddress);
vm.expectRevert(); // Would fail anyway
festival.createPerformance(block.timestamp + 1 hours, 1 hours, 100 ether);
vm.stopPrank();
// Festival is now permanently broken for organizer functions
}
function testMaliciousOrganizerTakeover() public {
address maliciousActor = makeAddr("malicious");
// If owner is compromised
vm.startPrank(owner);
festival.setOrganizer(maliciousActor);
vm.stopPrank();
// Malicious organizer can now manipulate everything
vm.startPrank(maliciousActor);
// Create performance with extreme rewards
festival.createPerformance(block.timestamp + 1 hours, 1 hours, type(uint256).max);
// Manipulate pass prices
festival.configurePass(GENERAL_PASS, 1 wei, type(uint256).max);
vm.stopPrank();
}

Recommended Mitigation

+ address public pendingOrganizer;
+ uint256 public organizerChangeDelay = 2 days;
+ uint256 public pendingOrganizerTimestamp;
+ event OrganizerChangeInitiated(address indexed newOrganizer, uint256 effectiveTime);
+ event OrganizerChanged(address indexed oldOrganizer, address indexed newOrganizer);
- function setOrganizer(address _organizer) public onlyOwner {
- organizer = _organizer;
- }
+ function initiateOrganizerChange(address _newOrganizer) public onlyOwner {
+ require(_newOrganizer != address(0), "Invalid organizer address");
+ require(_newOrganizer != organizer, "Same organizer");
+
+ pendingOrganizer = _newOrganizer;
+ pendingOrganizerTimestamp = block.timestamp + organizerChangeDelay;
+
+ emit OrganizerChangeInitiated(_newOrganizer, pendingOrganizerTimestamp);
+ }
+ function acceptOrganizerRole() public {
+ require(msg.sender == pendingOrganizer, "Not pending organizer");
+ require(block.timestamp >= pendingOrganizerTimestamp, "Change delay not met");
+
+ address oldOrganizer = organizer;
+ organizer = pendingOrganizer;
+ pendingOrganizer = address(0);
+ pendingOrganizerTimestamp = 0;
+
+ emit OrganizerChanged(oldOrganizer, organizer);
+ }
+ function cancelOrganizerChange() public onlyOwner {
+ pendingOrganizer = address(0);
+ pendingOrganizerTimestamp = 0;
+ }
Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Missing events / Events not properly configured

Informational. This protocol doesn't rely on events to function, they are just nice to have, but not mandatory.

Support

FAQs

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