Root + Impact
Description
When a function accepts an address argument that is meant to represent a valid user, contract, or owner, it is a crucial security and robustness best practice to include a check to ensure that the provided address is not the zero address (address(0x0)).
Several functions across FestivalPass.sol, BeatToken.sol, and inherited OpenZeppelin contracts lack these essential zero-address checks. This omission can lead to serious issues such as accidental loss of administrative control, permanent freezing of funds, or rendering core contract functionalities unusable if a critical address is inadvertently or maliciously set to address(0).
SLITHER OUTPUT:
## missing-zero-check Impact: Low
Confidence: Medium - [ ] ID-8
[FestivalPass.setOrganizer(address)._organizer](src/FestivalPass.sol#L50) lacks a zero-check on :
- [organizer = _organizer](src/FestivalPass.sol#L51)
src/FestivalPass.sol#L50
- [ ] ID-9
[Ownable2Step.transferOwnership(address).newOwner](lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol#L43) lacks a zero-check on :
- [_pendingOwner = newOwner](lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol#L44)
lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol#L43
- [ ] ID-10 [FestivalPass.withdraw(address).target](src/FestivalPass.sol#L147) lacks a zero-check on : - [address(target).transfer(address(this).balance)](src/FestivalPass.sol#L148)
src/FestivalPass.sol#L147
- [ ] ID-11 [BeatToken.setFestivalContract(address)._festival](src/BeatToken.sol#L13) lacks a zero-check on :
- [festivalContract = _festival](src/BeatToken.sol#L15)
src/BeatToken.sol#L13
- [ ] ID-12
[FestivalPass.constructor(address,address)._beatToken](src/FestivalPass.sol#L45) lacks a zero-check on :
- [beatToken = _beatToken](src/FestivalPass.sol#L47)
src/FestivalPass.sol#L45
src/FestivalPass.sol#L50
function setOrganizer(address _organizer) public onlyOwner {
organizer = _organizer;
}
lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol#L43
function transferOwnership(address newOwner) public virtual override onlyOwner {
_transferOwnership(newOwner);
}
src/FestivalPass.sol#L147
function withdraw(address target) public onlyOwner {
address(target).transfer(address(this).balance);
}
src/BeatToken.sol#L13
function setFestivalContract(address _festival) external onlyOwner {
festivalContract = _festival;
}
src/FestivalPass.sol#L45
constructor(
address _owner,
address _beatToken
) ERC1155("") Ownable2Step(_owner) {
beatToken = _beatToken;
}
Risk
Likelihood:
-
This will occur if a user or administrator mistakenly provides address(0) as an input to any of the affected functions.
-
This will occur if an attacker intentionally passes address(0) in an attempt to disable functionality or gain control (e.g., setting owner to zero address).
Impact:
Loss of Control: Setting administrative roles (like owner or organizer) to address(0) effectively renders the contract unmanageable, as the zero address cannot initiate transactions.
Irrecoverable Funds: Sending funds to address(0) (as in the withdraw function) means the funds are permanently lost and unrecoverable.
Broken Functionality: If critical contract dependencies (like beatToken or festivalContract) are set to address(0), functions relying on these addresses will likely revert or behave unexpectedly.
Deployment Errors: If crucial constructor parameters are address(0), the contract might be deployed in an unusable or compromised state.
Proof of Concept
Recommended Mitigation
- remove this code
+ add this code
@@ -49,5 +49,6 @@
function setOrganizer(address _organizer) public onlyOwner {
+ require(_organizer != address(0), "Organizer cannot be the zero address"); // @> Add zero-address check
organizer = _organizer;
emit OrganizerChanged(oldOrganizer, _organizer);
}
@@ -146,5 +147,6 @@
function withdraw(address target) public onlyOwner {
+ require(target != address(0), "Withdraw target cannot be the zero address"); // @> Add zero-address check
address(target).transfer(address(this).balance);
}
@@ -44,5 +45,6 @@
address _beatToken
) ERC1155("") Ownable2Step(_owner) {
+ require(_beatToken != address(0), "Beat token address cannot be zero"); // @> Add zero-address check
beatToken = _beatToken;
}