A few days later, we found one of those critical vulnerabilities affected the production REP token. In a nutshell, an out-of-bounds write on the token’s
reputation array allowed an attacker to modify the
creation timestamp, making it believe the crowdsale was still ongoing, and disabling all token transfers. This critical severity vulnerability, if exploited, could have halted the whole REP economy, worth over $200 million.
The Complete Story
We contacted the Augur team on July 13th 2017 to privately notify them about the issue. It is important to note that the critical vulnerability was in the Serpent compiler’s code, not in Augur’s code. We proposed a mitigation plan to reduce damages to the Augur project, which was accepted by their team. It included:
1- Writing a new REP smart contract in Solidity, based on OpenZeppelin’s reusable components.
2- Auditing the new REP token contract.
3- Disclosing to exchanges and wallet providers on July 24th 2017, 25 hours before public disclosure.
4- Deploying the new REP token contract to the Ethereum blockchain.
5- Freezing the old REP token (by exploiting the vulnerability ourselves) on July 25th 2017.
6- Migrating the balances of the frozen REP token into the new REP token.
The new token was developed by the Augur team using OpenZeppelin, and audited by the Zeppelin Solutions team. Deployment, migration and related scripts were also audited by the Zeppelin Solutions team.
REP Vulnerability Explained
To understand how the old REP token could be frozen, we first need to understand the Serpent critical severity vulnerabilities used in the attack.
First, Serpent contracts can overwrite storage locations when accessing arrays out of bounds. This means that if a Serpent contract attempts to access an array at a position greater than the array’s length, Serpent won’t stop it.
Second, the Serpent language is untyped. It allows any operation to be performed on any data. Every value is a 256-bit sequence which can be used as an address, a contract, an integer, or an array. Moreover, it performs no checks on the data sent by a user on a transaction.
This got us thinking if there was a way to force the REP contract to overwrite a storage location by sending unexpected data in one of its function parameters. Turns out there is.
Here’s the old REP token contract source code in case you want to guess how the attack works yourself with the information given so far.