The DAO's splitDAO() function let a token holder fork off into a child DAO, transferring their share of ether to the new DAO contract via a call to withdrawRewardFor(msg.sender) before zeroing the caller's balances[msg.sender]. withdrawRewardFor in turn invoked payOut(), which executed a raw `recipient.call.value(amount)()` against the caller's address. Because Solidity's .call forwarded all remaining gas and the caller was a contract, its fallback function ran inside the still-open splitDAO frame and re-entered splitDAO with the same parameters. The balance had not yet been zeroed (the assignment came after the external call — the canonical Checks-Effects-Interactions violation), so each recursive frame moved another tranche of ether to the attacker's child DAO. The attacker drained ~3.64 million ETH (about $60M at the time) into a child DAO that, by The DAO's own rules, was locked for 28 days — a window that catalysed the contentious July 2016 hard fork creating ETH/ETC.
Classification: Protocol Logic. Technique: Reentrancy. Target type: DeFi Protocol. Affected chains: Ethereum. Implementation language: Solidity.
- chain
- ethereum
- protocol
- The DAO
- bug_class
- reentrancy
- date_occurred
- 2016-06-17
- loss_usd
- $60,000,000
- classification
- Protocol Logic
- technique
- Reentrancy
- target_type
- DeFi Protocol
- language
- Solidity
- source_id
- dl:adhoc:the-dao:1466121600