The DAO Hack: The $60 Million Attack That Split Ethereum

On June 17, 2016, an attacker drained 3.6 million ETH from The DAO—roughly $60 million at the time. The mechanism was a reentrancy vulnerability in the withdrawal function. The consequence was a hard fork of Ethereum itself, and the permanent split of the chain into Ethereum and Ethereum Classic.

What Was The DAO?

The DAO was a decentralized venture fund built on Ethereum. Launched in April 2016, it raised approximately $150 million worth of ETH from over 11,000 participants—around 14% of all Ether in circulation at the time. The design was straightforward: token holders voted on investment proposals, and the contract automatically dispersed funds to approved projects.

It was also one of the most complex Solidity contracts deployed to that point.

The Vulnerability

The attack targeted The DAO’s splitDAO function, which let investors exit by withdrawing their share of funds into a new “child DAO.” The vulnerability was a textbook reentrancy bug: the contract sent ETH to the caller before updating its internal balance records.

// Simplified vulnerable pattern from The DAO
function splitDAO(uint _proposalID, address _newCurator) {
    // Calculate share of tokens
    uint fundsToBeMoved = (balances[msg.sender] * p.splitData[0].totalSupply) /
                          p.splitData[0].splitBalance;

    // VULNERABLE: External call BEFORE state update
    if (!p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender)) {
        throw;
    }

    // Balance zeroed AFTER the call — too late
    balances[msg.sender] = 0;
}

When the contract sent ETH to the recipient’s address, it triggered the recipient’s fallback function. If that address was a contract, the fallback could immediately call back into splitDAO before the original call had a chance to zero out the balance. Because the balance hadn’t been updated yet, the check passed again, and another withdrawal went through. This loop continued until the attacker stopped it.

The attacker’s contract was simple:

contract Attacker {
    DAO public dao;
    uint public count;

    fallback() external payable {
        if (count < 100) {
            count++;
            dao.splitDAO(proposalID, address(this));
        }
    }

    function attack() external {
        dao.splitDAO(proposalID, address(this));
    }
}

Why It Worked

Smart contract security practitioners now teach the Checks-Effects-Interactions (CEI) pattern precisely because of this attack. The rule is: verify your conditions, update your state, then make external calls—in that order, no exceptions. The DAO inverted steps two and three.

The second factor was Solidity’s fallback function. At the time, the language made it easy to overlook that sending ETH to an address is itself a control flow event. If the recipient is a contract, you’re handing execution over to arbitrary code before your function has finished.

Neither of these was an obscure edge case. There were public forum posts raising concerns about reentrancy in The DAO’s code before the attack. The warnings were noted but not acted on at the speed the situation required.

The Hard Fork

After the attack, the Ethereum community faced a decision with no clean answer. One option was to accept the loss: the code had run as written, the blockchain was immutable, and the attacker’s claim to the funds was—in a purely technical sense—legitimate. The other option was to execute a hard fork to roll back the transactions and return the stolen funds.

After weeks of contentious debate, Ethereum executed the hard fork on July 20, 2016, at block 1,920,000. The stolen funds were returned to investors through a recovery contract.

Not everyone accepted this outcome. A portion of the community continued on the original, unforked chain under the principle that “code is law”—that blockchain immutability should not bend to political consensus. That chain became Ethereum Classic (ETC). The attacker’s funds remained intact on ETC, worth around $8.5 million at the time of the split.

The philosophical divide created that day—between pragmatic intervention and absolute immutability—still runs through blockchain culture.

The Fix

The CEI pattern, combined with reentrancy guards, eliminates this class of vulnerability:

bool private locked;

modifier noReentrant() {
    require(!locked, "Reentrant call");
    locked = true;
    _;
    locked = false;
}

function withdraw() external noReentrant {
    uint amount = balances[msg.sender];
    balances[msg.sender] = 0;   // State updated first
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
}

OpenZeppelin’s ReentrancyGuard provides a production-hardened version of this pattern that most modern contracts use directly.

The DAO hack did not reveal a flaw in Solidity that was impossible to prevent. It revealed that a contract managing $150 million had shipped with a well-known vulnerability class that the community had already identified. The lesson wasn’t about the technology—it was about the gap between awareness and action.


References