Making such a critical change in a single step is error-prone and can lead to irrevocable mistakes. Since _newOwner could also be mistakenly set to any valid non-existing account addresses or existing incorrect smart contract addresses, any of which will lead to permenant loss of control on the SmartAccount wallet.
Due the following reasons, this issue is considered as medium severity:
Considering the intended users of the SmartAccount wallets, when ownership change is requested, the address of the new owner will have to be provided by ORDINARY users, there is a quite high probability of supplying an incorrect address; This is why there are so many tokens/ethers are locked on blockchian.
Also considering that SmartAccount wallet will be used as a convenient flexible smart wallet, it is likely that a SmartAccount wallet contains user's significant assets. Loss of control to the SmartAccount will incur permenant significant loss to users. Users may not be able to accept such loss due to ownership change;
Any loss due to ownership change is the entire loss of a SmartAccount wallet instead of the loss of a single transation;
And the worse, there is no way to rescue it as long as the incorrect owner address is set;
This may have negative implication on the reputation of the SmartAccount wallet.
Proof of Concept
A valid non-zero non-existing address _newOwner is used with function call SmartAccount.setOwner(_newOwner) initiated by the current owner. The contract owner is handed to _newOwner.
All privileged functions protected by onlyOwner or mixedAuth (e.g. setOwner, updateImplementation, updateEntryPoint, transfer, pullTokens, execute, executeBatch) are no longer callable, since the _newOwner does not exist, there is no way to call the above protected functions. Note: since there is no pre-coded code in the contract to call above functions by the contract itself, the functions protected by mixedAuth are also not callable.
The upgrading functions updateImplementation, updateEntryPoint are protected by mixedAuth. They are no longer callable, which means the issue cannot be solved through implementation upgrading.
Since _newOwner does not exist, it cannot be used to sign transactions. Thus, the SmartAccount cannot sign transactions using _newOwner to undertake privileged tasks such as changing ownership, transferring assets, etc.
Transactions executed through execTransaction function must be signed by the owner directly or indirectly (through EIP1271). Thus, they cannot be used to solve the issue in question.
Any assets in the contract are no longer transferrable.
There is no way to recover it.
Tools Used
Manual analysis.
Recommended Mitigation Steps
It is important to safely change ownership. A two-step ownership change process is recommended:
Step1: the owner calls a function to set the new owner's address to a state variable, say _newOwner. The function should be protected by onlyOwner;
Step 2: the new owner calls the switch owner function to actually switch owner address. The function verifies: msg.sender == _newOwner.
This process guarantees that the ownership will be correctly handed to an intended one.
Lines of code
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L109-L114
Vulnerability details
Impact
Contract
SmartAccount
performs a one-step ownership change by callingsetOwner
function, which only verifies_newOwner != address(0)
:Making such a critical change in a single step is error-prone and can lead to irrevocable mistakes. Since
_newOwner
could also be mistakenly set to any valid non-existing account addresses or existing incorrect smart contract addresses, any of which will lead to permenant loss of control on theSmartAccount
wallet.Due the following reasons, this issue is considered as medium severity:
SmartAccount
wallets, when ownership change is requested, the address of the new owner will have to be provided by ORDINARY users, there is a quite high probability of supplying an incorrect address; This is why there are so many tokens/ethers are locked on blockchian.SmartAccount
wallet will be used as a convenient flexiblesmart wallet
, it is likely that aSmartAccount
wallet contains user's significant assets. Loss of control to theSmartAccount
will incur permenant significant loss to users. Users may not be able to accept such loss due to ownership change;SmartAccount
wallet instead of the loss of a single transation;SmartAccount
wallet.Proof of Concept
_newOwner
is used with function callSmartAccount.setOwner(_newOwner)
initiated by the current owner. The contract owner is handed to_newOwner
.onlyOwner
ormixedAuth
(e.g.setOwner, updateImplementation, updateEntryPoint, transfer, pullTokens, execute, executeBatch
) are no longer callable, since the_newOwner
does not exist, there is no way to call the above protected functions. Note: since there is no pre-coded code in the contract to call above functions by the contract itself, the functions protected bymixedAuth
are also not callable.updateImplementation, updateEntryPoint
are protected bymixedAuth
. They are no longer callable, which means the issue cannot be solved through implementation upgrading._newOwner
does not exist, it cannot be used to sign transactions. Thus, theSmartAccount
cannot sign transactions using_newOwner
to undertake privileged tasks such as changing ownership, transferring assets, etc.execTransaction
function must be signed by theowner
directly or indirectly (through EIP1271). Thus, they cannot be used to solve the issue in question.Tools Used
Manual analysis.
Recommended Mitigation Steps
It is important to safely change ownership. A two-step ownership change process is recommended:
_newOwner
. The function should be protected byonlyOwner
;msg.sender == _newOwner
. This process guarantees that the ownership will be correctly handed to an intended one.