Reentrancy Attacks on Smart Contracts Distilled

Greetings, dear readers! We continue our series of educational articles and today we’ll look at Reentrancy Attack — one of the attacks that used to be very common in the Web3 smart contract-based projects, but has recently been relegated to the background, although they still pose a great danger!

Read an original article via the link below:

For example, there have been cases such as:

  • Uniswap/Lendf.Me hacks (April 2020) — $25 million

  • The BurgerSwap hack (May 2021) — $7.2 million

  • The SURGEBNB hack (August 2021) — $4 million

  • CREAM FINANCE hack (August 2021) — $18.8 million

  • Siren protocol hack (September 2021) — $3.5 million

First and foremost, we would like to express our sincere gratitude to the Authors of all the resource materials, and of course our pessimistic.io auditors who have helped so much by revealing needed information and lifting the curtain of secrecy. And today, dear readers, it will be made available to you.

To begin with, we will understand what Reentrancy is, what tools are currently available to detect it and, most importantly, how to reliably protect your project and your users from this type of attack. At the very end you will find a list of resources for more in-depth study.

We will share our own observations and give some advice in the next article in our series, since our team has been working since 2016, we have accumulated quite a few of them!

In this series we will focus only on those aspects that can be really useful for auditing and bug bounty hacking and that are not described anywhere! We can confidently say that such tips can be read publicly in a few places, and our blog is one of those places. Stay tuned!


I — What does Reentrancy stand for?

The issues surrounding Reentrancy in smart contracts, as is often the case with blockchain technology, do not originate in blockchain, but rather provide a novel and complex example of them.

Reentrancy is a term that has been used in computing for a long time to describe the process by which a process can be stopped in the middle of execution, a new occurrence of the same function can start, and both processes can then terminate.

arxiv.org/pdf/2106.10740.pdf
arxiv.org/pdf/2106.10740.pdf

We use reentrant functions in computing safely everyday. One good example is being able to begin an email draft in a server, exiting it to send another email, then being able to return to the draft to finish and send it.

Blockchain is a bit different — for example: If the target of an external call is a malicious contract controlled by the attacker, the attacker can execute the malicious logic when the attacked contract calls the malicious contract.

Then it will re-enter the attacked contract to make an unexpected external call, affecting the attack contract’s standard execution logic:

quantstamp.com/blog/what-is-a-re-entrancy-attack
quantstamp.com/blog/what-is-a-re-entrancy-attack

In simple terms, a Reentrancy attack occurs between two smart contracts, where an attacking smart contract exploits the code in a vulnerable contract to drain it of its funds.

The exploit works by having the attacking smart contract repeatedly call the withdraw function before the vulnerable smart contract has had time to update the balance:

www.researchgate.net/publication/329705851_Sereum_Protecting_Existing_Smart_Contracts_Against_Re-Entrancy_Attacks
www.researchgate.net/publication/329705851_Sereum_Protecting_Existing_Smart_Contracts_Against_Re-Entrancy_Attacks

This is only possible because of the order in which the vulnerable smart contract handles transactions, with the vulnerable smart contract first checking its balance, then sending funds, and finally updating its balance.

The time between sending the funds and updating the balance creates a window in which the attacking smart contract can make another call to withdraw its funds, and so on until all funds are drained.

cryptomarketpool.com/reentrancy-attack-in-a-solidity-smart-contract
cryptomarketpool.com/reentrancy-attack-in-a-solidity-smart-contract

Most Reentrancy attacks are done by reentering the same function (Single Function Reentrancy) it is called from; however, there are also other variations that are harder to discover and prevent.

We won’t go into detail about each of these in this article, but we’ll leave a couple of links below for you to explore it in more depth!

Types of Reentrancy:

The first two variations are commonly found, the examples can be discovered in Consensys’s Ethereum Smart Contract Best Practices.

Cross-Contract Reentrancy is described in this article and this post-mortem;

Cross-Function Reentrancy is described in this article.

Below is the contract, which contains the typical Single Function Reentrancy vulnerability, taken from this write-up:

contract DepositFunds {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);

        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }


}

The vulnerability comes where we send the user their requested amount of ether. In this case, the attacker calls withdraw() function. Since his balance has not yet been set to 0, he is able to transfer the tokens even though he has already received tokens.

Now, let’s consider a malicious attacker creating the following contract:

contract Attack {
    DepositFunds public depositFunds;

    constructor(address _depositFundsAddress) {
        depositFunds = DepositFunds(_depositFundsAddress);
    }

    // Fallback is called when DepositFunds sends Ether to this contract.
    fallback() external payable {
        if (address(depositFunds).balance >= 1 ether) {
            depositFunds.withdraw();
        }
    }

    function attack() external payable {
        require(msg.value >= 1 ether);
        depositFunds.deposit{value: 1 ether}();
        depositFunds.withdraw();
    }


}

The attack function calls the withdraw function in the victim’s contract. When the token is received, the fallback function calls back the withdraw function. Since the check is passed contract sends the token to the attacker, which triggers the fallback function.

For a deeper understanding of this attack, we advise you to check out this awesome Ethernaut level which exploits Reentrancy issue and the following, additional factors that led to the DAO hack:

If you are facing any troubles in reproducing the Reentrancy attack in Ethernaut, follow this walkthrough.

Read-Only Reentrancy:

The read-only reentrancy is a Reentrancy scenario where a view the function is reentered which in most cases is unguarded as it does not modify the contract’s state.

However, if the state is inconsistent, wrong values could be reported. Check out this blog and this article to know more about this.

For a better understanding of Reentrancy attack variations, visit this amazing chronological and (hopefully) complete list of Reentrancy attacks to date.

Check out:


II — How can we avoid Reentrancy?

To establish already existing reentrancy, auditors use different tools and methods — there are a lot of them. Just take a look at an awesome selection below and choose the one that will work out for you the best.

In our previous articles, we described Slither and fuzzing tool Echidna — be sure to check them out first!

The majority of existing tools, both free and paid, can be used to detect this type of vulnerability. However, some of them can detect only one type of vulnerability while others can detect all of them.

Here is a list of some of the best tools for locating those vulnerabilities:

From our perspective, Slither is certainly the best at finding such vulnerabilities, but keep in mind that you need to configure it properly!

arxiv.org/pdf/2112.03426.pdf
arxiv.org/pdf/2112.03426.pdf

Pre-auditing the code and using the tools and tips listed above will assist you in detecting Reentrancy, but I believe you’ll agree that it’s better not to let this happen at all.

All of this can be avoided if you improve the quality of your code, and we will now tell you exactly what you should take into account:

  • When writing code, you need to follow the coding standard (Checks-Effects-Interactions) of the first judgment and then write variables in external calls;

  • Add a Reentrancy guard; this prevents more than one function from being executed at a time by locking the contract.

The following is a code example of a reentry guard:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;contract ReEntrancyGuard {
bool internal locked;modifier noReentrant() {
require(!locked, “No re-entrancy”);
locked = true;
_;
locked = false;
}}

Auditors also suggest paying attention to the characteristics of reentrance vulnerabilities: all code locations involved in external contract calls are insecure. You need to focus on external calls and then deduce the possible harm of external calls to judge whether they can be exploited due to the reentry point.

Using CEI for all trusted contracts may be used with caution in order to prevent cross-contract read-only Reentrancy.

You can also take a more experimental route with using this awesome framework that combines static and dynamic analysis aiming to detect Reentrancy vulnerabilities in Ethereum smart contracts:

arxiv.org/pdf/2105.02881.pdf
arxiv.org/pdf/2105.02881.pdf

In a nutshell, it generates an attacker contract based on the ABI specifications of the smart contracts under test and analyzes the contract interaction to precisely report potential reentrancy.

The results appear promising, and we strongly advise you to incorporate dual check methodology into your workflow as a pre-audit stage!

It's important to mention that the Authors have not presented the tool yet, so we are only evaluating the methodology of the combining two different tools for a better Reentrancy check, but be sure that as soon as the tool is released — we will make its review in our blog!

We would like to express our sincere gratitude to the Authors of all the resource materials!

Check them out:


Support is very important to me, with it I can do what I love — educating users!

If you want to support my work, you can send me a donation to the address:

Subscribe to Officer's Blog
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.