What is a Re-entrancy Attack?

What is a reentrancy attack?

Blockchain hacks are on the rise, with an estimated $1 billion having already been stolen in hacks in 2022. Harmony became the latest victim of a large-scale attack with over $100 million being stolen from their cross-chain bridge this month. Today we’re going to take a look at one of the most common attacks in blockchain, and that is reentrancy attacks.

The chances are that if you follow crypto news, you will have heard the term reentrancy attack before. In the simplest terms, a reentrancy attack occurs between two smart contracts, when a function makes an external call to another untrusted contract. Then the untrusted contract makes a recursive call back to the original function in an attempt to drain funds.

A simple example of a reentrancy attack is when a contract carries out internal accounting with a balance variable and exposes a withdrawal function. If the vulnerable contract transfers funds before it sets the balance to zero an attacker can recursively call the withdraw function repeatedly and drain the whole contract of funds.

Reentrancy Attack Overview

A reentrancy attack involves two smart contracts. A protocol or victim's contract that contains bad code and an untrusted attacker's contract.

  • Contract A calls a function in Contract B
  • Then Contract B calls back into Contract A while Contract A is still processing

As you can see in the diagram above, the attacker makes a call on Contract A to transfer funds to Contract B. When receiving the call, Contract A begins by checking to see that the attacker has the required funds, and then it transfers the funds to Contract B. When it receives the funds, Contract B then executes a fallback function which calls back into Contract A before it is able to update its balance, in turn restarting the process again.

History of Reentrancy Attacks

Reentrancy is not a new term and is one that has been present in computing for a long time. It simply refers to a process which can be interrupted mid-way through execution, have a different occurrence of the same function begin, and then have both processes finish to completion.

Most of us regularly use reentrancy functions safely every day. One simple example is when you draft an email in a server, exit it to send another email, and then come back to the draft to finish and send it.

Blockchain Reentrancy Attacks

The most famous reentrancy attack in blockchain took place in 2016 with The DAO Hack. The DAO was a decentralized autonomous organization (DAO) that was launched in 2016 on the Ethereum blockchain.

After three weeks, The DAO had raised more than $150 million from over 11,000 investors, making it one of the largest crowdfunding campaigns in history at the time.

From the start, there were question marks over the project’s smart contracts, with reentrancy concerns being at the top of this list. In response to these concerns, the dev team did set about fixing the reentrancy bug, but not before a hacker managed to exploit the platform for 3.6 million Ether.

This hack marked a turning point in both the Ethereum Blockchain and Smart Contract Security. In response to the attack, Ethereum founder, Vitalik Buterin proposed a soft fork. In response to this, the DAO Hacker claimed that they would prevent any soft fork by bribing Ethereum miners. This, as well as a bug which was found in the update, led Ethereum to conduct a hard fork.

In effect, the hard fork rolled the Ethereum network’s history back to before the DAO Hack and reallocated the DAO’s ether to a different smart contract to allow investors to withdraw their funds. This hard fork was extremely controversial though as it went directly against the practice that blockchains are supposed to be immutable and censorship-resistant.

How Do I Prevent Reentrancy Attacks?

Following the DAO Hack, reentrancy checks have become part of any good smart contract audit and the good news is that reentrancy attacks can be prevented.

To prevent a reentrancy attack in a Solidity smart contract, you should:

  • Make sure that all state changes happen before calling an external contract
  • Use function modifiers that prevent reentrancy