The SpookySwap contract incorrectly initializes the Ownable constructor by passing msg.sender as a parameter. This misusage results in the contract owner not being set properly, leading to a critical access control vulnerability where any user can execute functions intended only for the owner.
The SpookySwap contract inherits from OpenZeppelin's Ownable contract but incorrectly attempts to pass msg.sender to the Ownable constructor:
In OpenZeppelin's Ownable contract, the constructor does not accept any parameters. Its correct usage initializes the owner to the account deploying the contract (msg.sender) without requiring any arguments. By incorrectly passing msg.sender to the Ownable constructor, the intended owner is not set, and the owner state variable remains at its default value.
As a result, the onlyOwner modifier used throughout the SpookySwap contract does not function as intended. Functions protected by onlyOwner, such as addTreat, setTreatCost, withdrawFees, and changeOwner, become accessible to any user, compromising the contract's access control mechanisms.
Unauthorized Access: Any user can invoke functions that are meant to be restricted to the contract owner.
Financial Loss: Malicious users can withdraw all Ether from the contract using the withdrawFees function.
Data Integrity Issues: Attackers can add or modify treats, altering costs and metadata, which can disrupt the contract's intended functionality.
Ownership Takeover: An attacker can change the contract's ownership to themselves using the changeOwner function, gaining full control over all owner-restricted functions.
Incorrect Constructor Usage:
The contract attempts to initialize the Ownable constructor with msg.sender:
However, OpenZeppelin's Ownable contract constructor is defined without parameters:
Owner Not Set Properly:
Due to the incorrect constructor call, the owner variable is not initialized to the deploying address. It either remains the zero address (address(0)) or is not set at all.
Bypassing onlyOwner Restrictions:
Since the owner is not correctly set, the onlyOwner modifier does not restrict access. For example, any user can call the withdrawFees function:
Exploiting the Vulnerability:
Withdraw Funds:
An attacker calls withdrawFees:
This transfers all Ether in the contract to the incorrect owner address, which may be address(0) or some unintended address.
Change Ownership:
An attacker can become the owner:
The attacker now controls all onlyOwner functions.
Modify Treats:
The attacker can add or modify treats:
Correct the Ownable Inheritance and Constructor Call:
Remove the incorrect parameter from the Ownable inheritance:
Initialize the Ownable contract properly in the constructor:
Verify Owner Initialization:
Ensure that the owner is correctly set to the deploying address (msg.sender) by default when the contract is deployed.
Audit Access Control Modifiers:
After correcting the inheritance, review all functions using the onlyOwner modifier to confirm they enforce access restrictions as intended.
Test the Deployment:
Deploy the corrected contract in a testing environment and verify that only the owner can call onlyOwner functions.
Update Documentation:
Reflect the changes in any associated documentation to prevent future misunderstandings regarding constructor usage.
By implementing these recommendations, the contract will correctly set the owner upon deployment, ensuring that access control mechanisms function as intended and protecting against unauthorized actions.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.