# uninitialized proxy contracts are vulnerable to re-initialization attacks that hijack ownership In proxy patterns, constructors run only during deployment and operate on the implementation contract's storage, not the proxy's. Initializer functions replace constructors but require explicit guards preventing multiple executions. When guards are missing, an attacker can call the initializer to claim ownership. **Incidents:** Wormhole (2022) was left uninitialized after a routine update; the attacker called `initialize()` on the implementation, became the guardian, and used upgrade authority to point the proxy to a malicious implementation. AllianceBlock (August 2024) had an upgrade reset the initialized boolean, enabling re-initialization (see [[re-initialization vulnerabilities arise when upgrades inadvertently reset initialization state]]). Harvest Finance had uninitialized UUPS vault proxies that could have led to self-destruction ($200K bounty, Dedaub). KeeperDAO had ~$44M at risk from uninitialized UUPS implementations (Ashiq Amien, ~$50M total across 4 projects). **Mitigation:** Call `_disableInitializers()` in the implementation contract's constructor. OpenZeppelin published this fix in Contracts v4.3.2 after the September 2021 disclosure. Since [[openzeppelin initializable with initializer modifier prevents re-initialization attacks on proxy contracts]], the defense is well-established. The [[parity wallet hack demonstrated that selfdestruct in implementation contracts permanently bricks proxy systems|Parity wallet hack]] exploited exactly this: an uninitialized implementation allowed an attacker to call `initWallet`, claim ownership, then self-destruct. A sophisticated variant is CPIMP: since [[CPIMP attacks exploit the gap between proxy deployment and initialization to inject a persistent malicious intermediary that parasitizes the protocol invisibly]], rather than claiming ownership, CPIMP inserts a double-delegation intermediary forwarding calls to the legitimate implementation while granting a hidden super-admin channel. CPIMP front-runs the initialization transaction to write a shadow implementation address into the ERC1967 slot. The protocol works normally (harder to detect), the shadow reinstalls after every transaction (harder to remove), and the July 2025 wave affected EtherFi, Pendle, Orderly Network, and others. The `initializer` modifier does not prevent CPIMP because the attack occurs before the initializer runs. --- Relevant Notes: - [[openzeppelin initializable with initializer modifier prevents re-initialization attacks on proxy contracts]]: the standard defense - [[delegatecall executes code from another contract using the callers storage context]]: why constructors don't work for proxies - [[parity wallet hack demonstrated that selfdestruct in implementation contracts permanently bricks proxy systems]]: the canonical exploit of this vulnerability - [[access control vulnerabilities are the leading cause of smart contract financial losses]]: re-initialization is an access control failure - [[vyper module composition uses explicit uses initializes and exports keywords preventing accidental state coupling between modules]]: Vyper's `initializes` keyword prevents this class by enforcing exactly-once initialization at the language level - [[re-initialization vulnerabilities arise when upgrades inadvertently reset initialization state]]: the upgrade-triggered sibling; contract was properly initialized but an upgrade resets the guard - [[CPIMP attacks exploit the gap between proxy deployment and initialization to inject a persistent malicious intermediary that parasitizes the protocol invisibly]]: the sophisticated variant: same root vulnerability, but inserts a hidden parasitic intermediary rather than claiming ownership; survives upgrade attempts and requires atomic deployment to prevent - [[non-atomic proxy deployment creates a front-running window where any actor can write to the ERC1967 implementation storage slot before the legitimate initialization transaction confirms]]: the deployment pattern that enables CPIMP; shows the precise mechanism - [[Solidity functions without an explicit visibility modifier defaulted to public before version 0.5.0 allowing anyone to call intended-internal functions]] -- the pre-0.5.0 mechanism that originally exposed initialization functions to arbitrary callers; default-public visibility and the missing initializer guard share the same attack surface (a callable initialization function), making them historical and contemporary variants of the same vulnerability class Topics: - [[vulnerability-patterns]]