Beatland Festival

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

[ L-4] - Missing Zero-Address Validation

Missing Zero-Address Validation + Critical Parameter Corruption

Description

  • Normal Behavior:
    In secure smart contract design, all critical address parameters—such as those for token contracts, organizers, and withdrawal targets—should be validated to ensure they are not set to the zero address (address(0)). The zero address is a special value in Ethereum that represents "no address" and is often used as a sentinel value for uninitialized or invalid addresses. Allowing critical roles or funds to be assigned to the zero address can break contract functionality, cause loss of funds, or make the contract irrecoverable.

  • Issue:
    In the FestivalPass contract, several functions and the constructor do not validate that provided addresses are nonzero:

    • The constructor does not check that _beatToken or _organizer are nonzero.

    • The setOrganizer function does not check that the new organizer address is nonzero.

    • The withdraw function does not check that the withdrawal target is nonzero.
      This omission allows critical roles to be set to the zero address and funds to be sent to an address from which they can never be recovered.

  • Relevant Code Example:

// Constructor
constructor(address _beatToken, address _organizer) {
@> beatToken = _beatToken;
@> organizer = _organizer;
}
// setOrganizer
function setOrganizer(address _organizer) external onlyOwner {
@> organizer = _organizer;
emit OrganizerChanged(_organizer);
}
// withdraw
function withdraw(address target) external onlyOwner {
@> (bool success, ) = payable(target).call{value: address(this).balance}("");
require(success, "Transfer failed.");
}

Risk

Likelihood:

  • This will occur if a user, owner, or external system mistakenly or maliciously sets a critical address to zero.

  • It is a common source of bugs and exploits, especially in production deployments or when integrating with external systems.

Impact:

  • Loss of Funds: If withdraw is called with the zero address, all ETH in the contract is sent to an address from which it can never be recovered.

  • Loss of Control: If the organizer or token contract is set to the zero address, critical functionality (such as pass configuration, performance creation, or token minting) is permanently disabled.

  • Irrecoverable State: There is no way to recover from these errors without redeploying the contract, leading to loss of user funds, trust, and operational continuity.

Proof of Concept

To reproduce this issue, copy and paste the following test code into your test file (e.g., test/contract.t.sol). This test demonstrates that setting critical addresses to zero or withdrawing to the zero address results in loss of control or funds:

function test_MissingZeroAddressValidation() public {
// 1. Deploy with zero address for organizer
try new FestivalPass(address(beatToken), address(0)) {
// Should not succeed
assertTrue(false, "Deployment with zero organizer should fail");
} catch {}
// 2. Set organizer to zero address
vm.prank(owner);
festivalPass.setOrganizer(address(0));
assertEq(festivalPass.organizer(), address(0)); // Organizer is now zero, contract is broken
// 3. Withdraw to zero address
vm.deal(address(festivalPass), 1 ether);
vm.prank(owner);
festivalPass.withdraw(address(0));
// All funds are now lost, as address(0) cannot be recovered
assertEq(address(festivalPass).balance, 0);
}

Explanation:

  • The contract can be deployed with a zero organizer, breaking all organizer-only functionality.

  • The organizer can be set to zero, disabling all future pass/performance configuration.

  • ETH can be withdrawn to the zero address, resulting in permanent loss of funds.

Recommended Mitigation

Add zero-address checks to all critical address parameters in the constructor and relevant functions. This ensures that roles and funds cannot be assigned to the zero address.

// Constructor
constructor(address _beatToken, address _organizer) {
+ require(_beatToken != address(0), "BeatToken cannot be zero address");
+ require(_organizer != address(0), "Organizer cannot be zero address");
beatToken = _beatToken;
organizer = _organizer;
}
// setOrganizer
function setOrganizer(address _organizer) external onlyOwner {
+ require(_organizer != address(0), "Organizer cannot be zero address");
organizer = _organizer;
emit OrganizerChanged(_organizer);
}
// withdraw
function withdraw(address target) external onlyOwner {
+ require(target != address(0), "Target cannot be zero address");
(bool success, ) = payable(target).call{value: address(this).balance}("");
require(success, "Transfer failed.");
}

Summary:
Zero-address validation is a fundamental security and correctness requirement in Ethereum smart contracts. Failing to implement these checks can lead to catastrophic, irrecoverable failures. The recommended mitigations are simple, low-cost, and should be applied to all critical address parameters.

Updates

Lead Judging Commences

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

Zero address check

Owner/admin is trusted / Zero address check - Informational

Support

FAQs

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