Toutes les vulnérabilités
CRITICALWeb3exploited in the wild

WEB3-PROXY-COLLISION-2022

Web3 · Ethereum · Upgradeable delegatecall proxy (Audius)

Résumé

On July 23, 2022 the Audius governance, staking, and delegation contracts on Ethereum mainnet were drained of 18,564,497 AUDIO (~$6.1M) because their upgradeable delegatecall proxy had a storage-layout collision with a re-callable initializer. A delegatecall proxy runs the logic contract's bytecode against the proxy's own storage, so the two contracts must agree on every slot index; Audius added a variable to the proxy that occupied the same low slot the implementation used for the OpenZeppelin Initializable initialized flag. Writing the proxy-side value reset the implementation's initialized boolean to a non-true state, removing the one-time guard, so the attacker re-invoked initialize() against an already-deployed contract. Re-initialization let the attacker register themselves as governance guardian and submit a malicious proposal that delegated enormous voting weight and executed an immediate treasury transfer. The contracts had been audited by OpenZeppelin and Kudelski but the collision was introduced later and missed.

Comment l’éviter dans votre code

  • Use OpenZeppelin's initializer modifier plus _disableInitializers() in the implementation constructor.
  • Define proxy admin/impl in EIP-1967 namespaced slots, never low slots that collide with logic variables.
  • Run automated storage-layout diff (openzeppelin-upgrades) on every upgrade before deploy.
  • Append new state variables only at the end of the layout; never insert or reorder existing slots.
  • Verify on-chain that initialize() reverts post-deployment and that the initialized flag is set.

Références

Vulnérabilités liées

Tout Web3 →