Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Upgrade machanism lacks access control: `_authorizedUpgrade` does not implement access resctriction

Summary

_authorizedUpgrade does not implement any access resctriction. Consequently, anyone can upgrade the Mondrian wallet of any user to any new implementation contract.

Vulnerability Details

The upgrade mechanism from a current implementation contract to a new one can be initiated by calling MondrianWallet2::upgadeToAndCall (a function defined in the UUPSUpgradeable contract). This function then calls MondrianWallett::_authorizedUpgrade, a function that has to be implemented in the implementation contract as per required by UUPSUpgradeable, which contract also states:

" The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
...
Function that should revert when msg.sender is not authorized to upgrade the contract. Called by {upgradeToAndCall}.
Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
function authorizeUpgrade(address) internal onlyOwner {}"

However, MondiranWallet::_authorizeUpgrade does not actually implement any access control.

The following test demonstrates if any non-owner user initiates the upgrade mechaism by calling upgradeToAndCall, the upgrade runs successfully, as signified by the emitted event.

event Upgraded(address indexed implementation);
function testZkAnyoneCanUpgrade() public onlyZkSync {
address notOwner = makeAddr("notOwner");
// newImplementation has to be a comtract and need to implement _authorizeUpgrade(address newImplementation)
MondrianWallet2 newImplementation = new MondrianWallet2();
// Expect the Upgraded event
vm.expectEmit(true, true, true, true);
emit Upgraded(address(newImplementation)); // defining the expected event emission
vm.prank(notOwner);
mondrianWallet.upgradeToAndCall(address(newImplementation), "");
}

Impact

Malicious users can upgrade the Mondrian wallet of any user to any new implementation contract. They can implement whatever they want in the new implementation contract, including code that leads to loss of access and loss of funds for the true, original owner of the wallet.

Tools Used

Manual review, Foundry.

Recommendations

Restrict access to the owner as follows:

+ error MondrianWallet2__NotTheOwner();
function _authorizeUpgrade(address newImplementation) internal override {
+ if (msg.sender != owner()) {
+ revert MondrianWallet2__NotTheOwner;
+ }
}

You could consider granting access not only to the owner but also to the Bootloader, but in general it is best not to grant access to such critical functions to anyone but the owner.

Updates

Lead Judging Commences

bube Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Lack of access control in _authorizeUpgrade

Support

FAQs

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