In June 2016, an attacker exploited a reentrancy vulnerability in The DAO — one of the first major Ethereum DAOs — and drained 3.6 million ETH worth $60 million at the time. This single hack led to the Ethereum hard fork that created Ethereum Classic.
Eight years later, reentrancy attacks are still one of the most common smart contract vulnerabilities. Let's break down exactly how they work.
What is a Reentrancy Attack?
A reentrancy attack happens when a malicious contract calls back into the victim contract before the first execution finishes. If the victim contract sends ETH to an external address before updating its state, the attacker can re-enter the function and drain funds repeatedly.
Think of it like a bank ATM bug — you withdraw $100, and before your balance updates, you withdraw another $100, and another, until the ATM is empty.
The Vulnerable Pattern
When msg.sender.call is executed, if msg.sender is a malicious contract, its receive() or fallback() function runs. That function calls withdraw() again — and since balances[msg.sender] hasn't been set to 0 yet, it passes the check and drains more funds.
The Attack Contract
The Fix: Checks-Effects-Interactions Pattern
The golden rule: always update state BEFORE making external calls. This is called the Checks-Effects-Interactions (CEI) pattern.
Extra Protection: ReentrancyGuard
For extra safety, use OpenZeppelin's ReentrancyGuard. It adds a mutex lock that prevents re-entering any function marked with nonReentrant.
Quick Checklist
Before deploying any contract that sends ETH, ask yourself:
✅ Do I update state BEFORE external calls?
✅ Do I use ReentrancyGuard on sensitive functions?
✅ Have I tested with a malicious contract that re-enters?
✅ Did I run an automated audit?