July 26, 2022 Ali Ashar

Web3 security is more than smart contract audits | Audius Hacked, $6M Stolen

A long-overlooked vulnerability led an attacker to steal nearly 18.6 million AUDIO tokens from the Web3 streaming music service.

Audits are not the only priority when it comes to Web3 & Blockchain cybersecurity. Decentralized streaming music service Audius was hacked for more than $6 million worth of AUDIO tokens over the weekend, which the attacker stole from its governance smart contract. The Audius smart contracts had been audited by security groups—first by OpenZeppelin in August 2020, with further contract additions audited by Kudelski in October 2021. Even so, that vulnerability remained open in the public for nearly two years since the contracts were first deployed in October 2020.

Encountered a major setback yesterday when Audius’s governance proposal #85 was maliciously confirmed on the company’s website. Hackers managed to loot over 18 million of the platform’s native Audius (AUDIO) token. At its current exchange rate, the tokens are worth an approximate $6.2 million, however, after the fraudster decided to swap the Audius (AUDIO) into Ethereum (ETH), the attacker got away with slightly more than $1 million dollars.

After stealing 18 million tokens for $6 million, the hacker sold them for $1.08 million on decentralized exchange Uniswap, causing a slippage in the AUDIO token price. Slippage is the difference between the expected price of a token and the price when the order executes and can be expressed as a percentage or a dollar amount. One investor suggested a buyback to prevent a selloff that would drive the price down further. Another investor gave Audius an ultimatum: recover, else they are out.

Key Highlights

  • Audited contracts were compromised due to an exploit in the contract initialization code that allowed repeated invocations of the “initialize” function.
  • This allowed an attacker to modify the voting system and set erroneous stake values in the network, leading to a malicious transfer of 18MM $AUDIO tokens held by the Audius governance contract (referred to as the “community treasury”) their wallet.
  • All remaining funds are safe and fixes have been deployed. At this point, all remaining smart contract components have been updated and unpaused except staking & delegation. We expect to have these online within the next couple of days after changes have been reviewed.


The Audius governance contracts utilize the OpenZeppelin proxy upgradability pattern

Audius employs the AudiusAdminUpgradabilityProxy contract in order to permit proxy upgrades to the logic contracts of the Audius Governance system (e.g. Staking, Delegation). In its implementation, the AudiusAdminUpgradabilityProxy uses storage slot 0 for the address of  the proxyAdmin: [code]. The proxyAdmin for the Audius protocol was set to the governance system address of `0x4deca517d6817b6510798b7328f2314d3003abac` which implements various checks and balances to prevent unauthorized use (voting procedures, time delays, a community-run override process, etc.). ‍

This caused a collision with OpenZeppelin’s Initializable contract’s initialized and initializing boolean state, which are also stored in slot 0 (the first and second bytes). Because the last byte of the proxyAdmin address is `0xac`, initialized was interpreted as a truthy value. Similarly, because the second byte of the proxyAdmin address is `0xab`, initializing was also interpreted as a truthy value. This caused the initializer() modifier to always succeed:

require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); 

Furthermore, because `initializing` was already true, the call was not considered to be a `topLevelCall`, which meant that both `initializing` and `initialized` were left unchanged. This allowed for repeated invocations of any function which used the `initializer` modifier. Documentation for this form of attack vector & storage collision can be found [here].‍

Using this bug, the attacker was able to call the initializer method of deployed Audius contracts that implement Initializable and change storage state that is intended to be set only once in initialization. Specifically, the attacker called initialize on the Governance, Staking & DelegateManagerV2 contracts [source] [source].

With this, the attacker was able to (1) Re-define voting on the Audius protocol and modify the governance contract’s guardian address (2) Set the governance address of both the Staking & DelegateManagerV2 contracts to that of a custom deployment of the Audius governance contract 0xbdbb5945f252bc3466a319cdcc3ee8056bf2e569) and abuse the Audius protocol by

  1. Marking an erroneous delegation of 10,000,000,000,000 $AUDIO to themselves in an attempt to pass a governance vote. (No circulating supply impact / confined to the storage of Staking & Delegation contracts)
  2. Marking a second erroneous delegation of 10,000,000,000,000 $AUDIO to themselves in an attempt to pass a governance vote, which did pass and transferred the funds. (No circulating supply impact / confined to the storage of Staking & Delegation contracts)
  3. Transferring 18,564,497 $AUDIO tokens from the community treasury: Etherscan

Fortunately, the Audius team was able to develop and apply a patch to quickly regain control of the protocol before the attacker could do more damage.

On 25th July 2022, Audius team released a post-mortem report of the attack. Read the full report here: Audius Governance Takeover Post-Mortem 


, , , , , , , , , , , , , , ,

Stay in touch

Join the community