SmartAccount implementation can be destroyed by a bad actor #443
Labels
3 (High Risk)
Assets can be stolen/lost/compromised directly
bug
Something isn't working
duplicate-496
edited-by-warden
satisfactory
satisfies C4 submission criteria; eligible for awards
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Lines of code
https://github.com/code-423n4/2023-01-biconomy/blob/main/scw-contracts/contracts/smart-contract-wallet/SmartAccount.sol#L166
Vulnerability details
The SmartAccount wallet architecture is mainly defined by a
Proxy
contract that delegates functionality to a common reusableSmartAccount
implementation.The
SmartAccountFactory
contract has the address of a baseSmartAccount
implementation (immutable variable_defaultImpl
). When a new wallet is created, the factory deploys a newProxy
that points its implementation to this baseSmartAccount
implementation, and then calls theinit
function to initialize the wallet (sets owner, sets entrypoint, etc.).Each Proxy contract that represents a wallet holds its storage state and delegates functionality to a
SmartAccount
implementation. This implementation contract is shared by all wallets.Since the
SmartAccount
implementation is a contract that can be interacted with, a bad actor can initialize it and destroy this common implementation by:init
on the implementation contract to become the owner of the contract.execTransaction
, execute adelegatecall
to a contract that performs theselfdestruct
opcode.Impact
This security issue affects the functionality of existing wallets, rendering them unusable. The issue is caused by the destruction of the implementation contract code and the inability of the proxy to delegate functionality to the implementation contract.
As a result, owners will lose access to their wallets and any associated funds, as well as any third party integrations that depend on the wallet.
Additionally, the issue will prevent any upgrade functionality, as the upgrade logic is implemented in the same 'SmartAccount' contract using the 'Singleton' mixin and accessed through the 'updateImplementation' function. This means that upgrades will also be unavailable once the implementation contract is destroyed.
PoC
The following test reproduces the issue. An attacker calls
init
on the implementation contract and executes adelegatecall
to a simple contract that does theselfdestruct
.Note that in foundry tests,
selfdestruct
doesn't take effect until the test finalizes (see here).Recommendation
Add a constructor to the
SmartAccount
contract that calls OpenZeppelin_disableInitializers()
helper. This will disable any initializer in the implementation contract and prevent theinit
function (or any other initializer) from being called.The text was updated successfully, but these errors were encountered: