Explaining the DAO exploit for beginners in Solidity
Yes, another article about the DAO attack BUT this time it is for dummies ;). I mean, I want to explain the attack in simple terms and steps so beginners, and even non-technical Ethereum enthusiasts, can have a technical understanding of what happened to one of the most popular Ethereum applications. I’m not a software engineer nor an experienced Ethereum developer, I’m just a beginner!
The hacker exploited a bug in the code of the DAO and stole more or less $50 million worth of ether. I will focus here only on the main technical issue of the exploit: The fallback function. For a more detailed and advanced recount of the attack, the blog posts by Phil Daian and Peter Vessenes are highly recommended.
To start off, keep in mind that in Ethereum there are two types of accounts: (i) externally owned accounts controlled by humans and (ii) contract accounts controlled by code. This is important because only contract accounts have associated code, and hence, can have a fallback function.
In Ethereum all the action is triggered by transactions or messages (calls) set off by externally owned accounts. Those transactions can be an ether transfer or the triggering of contract code. Remember, contracts can trigger other contracts’ code as well.
Smart contracts are written in high-level programming languages such as Solidity but for those contracts to be uploaded on the blockchain, they need to be compiled into bytecode, a low-level programming language executed by the Ethereum Virtual Machine (EVM). Said bytecode can be interpreted with opcodes.
When a contract calls or sends money to another contract that code compiles in the EVM bytecode, invoking the call function. But, there is a difference: When calling another contract the call function provides specific function identifiers and data, however, when sending money to another contract, the call function has a set amount of gas but no data (case b below), and thus, triggers the fallback function of the called contract.
The fallback function abuse played a very important role in the DAO attack. Let’s see what a fallback function is and how it can be used for malicious purposes.
A contract can have one anonymous function, known as well as the fallback function. This function does not take any arguments and it is triggered in three cases :
a. If none of the functions of the call to the contract match any of the functions in the called contract
b. When the contract receives ether without extra data
c. If no data was supplied
The following is sample code for a contract vulnerable to a malicious fallback function of another contract. In this example we have two contracts: (i) the contract Bank (vulnerable contract) and (ii) the contract BankAttacker (malicious contract). Imagine that the contract Bank is the DAO smart contract but much more simplified and the contract BankAttacker is the hacker’s malicious smart contract that emptied the DAO.
The hacker initiates the interaction with contract Bank through its malicious contract and the sequence of the actions is as follows:
- The first thing the hacker does is send ether (75 wei) to the vulnerable contract through the deposit function of the malicious contract. This function calls the addToBalance function of the vulnerable contract.
- Then, the hacker withdraws, through the withdraw function of the malicious contract, the same amount of wei (75), triggering the withdrawBalance function of the vulnerable contract.
- The withdrawBalance function first sends ether (75 wei) to the malicious contract, triggering its fallback function, and last updates the userBalances variable (that this piece is done last is very important for the attack).
- The malicious fallback function calls the withdrawBalance function again (recursive call), doubling the withdraw, before the execution of the first withdrawBalance function finishes, and thus, without updating the userBalances variable.
In this example, there are only two recursive calls to the withdrawBalance function so the hacker ends up with a balance of 150 wei. They took more than they should (75 wei) because the userBalance variable is the last thing set/updated.
The following is a more graphic explanation of the example. The instances referred in this graphic are the different states of the contracts saved in the blockchain. In the graphic you will see that the hacker, through his/her/their external account, triggers the malicious contract, so this contract can interact with the vulnerable contract.
I’ve learned a lot understanding the DAO exploit, mainly that programming smart contracts is not an easy task and it should be done rigorously. I still have lots of unsolved questions such as: Do we need fallback functions at all? Apparently this was fixed in the new version of Solidity. However, the problem is still present at the EVM level because a hacker can program in opcode and avoid the Solidity’s security measures.
Meanwhile, I’m working with two friends David Jaramillo and Graziano Pirovano (great software engineers) on a software architecture project, which aims to protect programmers from the risks of malicious fallback functions within the current Ethereum ecosystem. More about this project to come!
Thank you to Graziano Pirovano y David Jaramillo.