// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /** * @title ReentrancyVault * @notice A vault with a classic reentrancy vulnerability in the withdraw function. * DO NOT use in production. For research only. * * Attack vector: External call tomsg.sender before updating internal state. * The victim calls withdraw() and the attacker recursively drains the vault. */ contract ReentrancyVault { mapping(address => uint256) public balances; event Deposit(address indexed user, uint256 amount); event Withdraw(address indexed user, uint256 amount); function deposit() external payable { balances[msg.sender] += msg.value; emit Deposit(msg.sender, msg.value); } // Vulnerability: external call before state update function withdraw(uint256 amount) external { require(balances[msg.sender] >= amount, "Insufficient balance"); // @audit-info - sends ETH before updating state (reentrancy vector) (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); balances[msg.sender] -= amount; emit Withdraw(msg.sender, amount); } function getBalance() external view returns (uint256) { return address(this).balance; } }