# Destruction of the SmartAccount implementation

Uninitialized SmartAccount implementation can be initialized by attacker then selfdestructed via delegatecall, freezing all wallets pointing to it.

- Payout: Contest reward
- Severity: high
- Program: Biconomy
- Credited: V\_B
- Disclosed: 2023-03-03
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-01-biconomy-findings/issues/496
- Library page: https://bugbountylibrary.com/r/c4%3A2023-01-biconomy%3AH-01
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:misconfiguration

**Target:** SmartAccount implementation contract / proxy wallet
**Root cause:** Deploy script never initializes the SmartAccount implementation, and init() is callable by anyone; the implementation also lacks a constructor locking owner, so it can be initialized and then destroyed.
**Steps:**
1. Deploy leaves baseImpl uninitialized (no init call in deploy script).
2. Attacker calls `init(...)` on the implementation contract to become owner.
3. Attacker submits a tx with a single `delegatecall` to a Destructor contract whose fallback runs `selfdestruct(payable(0))`.
4. Executor.sol#L23 delegatecall executes selfdestruct in the implementation's context, destroying it.
5. All proxies pointing to the implementation lose logic; Singleton upgrade path also destroyed.
**How found:** Code review of deploy scripts (wallet-factory.deploy.ts, deploy.ts) showed no initialization of the implementation; reading init/Executor delegatecall revealed the selfdestruct path, and checkSignatures/ecrecover zero-address behavior provided a fallback bypass.
**Exploitation:** Attacker initializes the uninitialized implementation to gain owner, then routes a delegatecall through Executor to a contract that selfdestructs, wiping the implementation code that every proxy wallet depends on.
**Escalation:** Even if init were blocked, owner==address(0) lets checkSignatures pass via ecrecover returning zero address for bad inputs, still enabling the delegatecall/selfdestruct path.
**Insight:** For proxy/implementation patterns, check whether the implementation is initialized at deploy and whether init is guarded; an uninitialized impl plus any delegatecall primitive is a selfdestruct/ownership-takeover vector. Also audit ecrecover==address(0) paths when owner is unset.
