Summary
On June 17, 2016, an attacker exploited a reentrancy vulnerability in The DAO's smart contract, draining around 3.6 million ETH (about $60-70M at the time). The vulnerable path was the splitDAO function, used to let dissenting holders exit into a child DAO: it sent ether via an external msg.sender.call.value() before it decremented balances[msg.sender] and totalSupply. Because the attacker's receiving contract had a fallback that recursively re-entered splitDAO before those state updates ran, the same balance was refunded over and over in a single nested call sequence, withdrawing far more ether than the attacker was owed. The DAO had raised roughly $150M in ETH after launching in spring 2016. To return the stolen funds, the Ethereum community executed a contentious hard fork on July 20, 2016, splitting the chain into Ethereum (funds returned) and Ethereum Classic (which kept the original 'code is law' chain).
How to avoid it in your code
- Follow checks-effects-interactions: update balances and totalSupply before any external call.
- Use a reentrancy guard (mutex) on functions that make external calls.
- Prefer pull-over-push withdrawals and minimize ether transfers inside state transitions.
- Limit gas forwarded on external calls or use transfer-style patterns where appropriate.
- Add reentrancy-focused invariant and property tests for every fund-moving function.
References
Related vulnerabilities
All Web3 →- CRITICALWEB3-PENPIE-2024
On September 3, 2024, Penpie, a yield protocol built on Pendle, was drained of about $27.3 million (11,113.6 ETH in wstETH, sUSDe, egETH and rswETH) across Ethereum and Arbitrum. The root cause was a cross-function reentrancy enabled by permissionless market registration: registerPenpiePool trusted any market from Pendle's PendleMarketFactoryV3 without validating the Standardized Yield (SY) token, so the attacker registered a fake market whose SY was their own contract. PendleStakingBaseUpg.batchHarvestMarketRewards (and its internal _harvestBatchMarketRewards) snapshotted reward-token balances before and after calling the market's redeemRewards, but lacked a nonReentrant guard. The malicious SY's claimRewards callback re-entered PendleStakingBaseUpg.depositMarket with flash-loaned Pendle LP tokens mid-accounting, so the deposit was misattributed as harvested rewards, inflating the attacker's reward balance. Although depositMarket itself carried a nonReentrant modifier, the two functions did not share a lock, so the unguarded harvest path let the attacker re-enter the guarded deposit path and claim the inflated rewards via MasterPenpie.multiclaim.
- CRITICALWEB3-CURVE-VYPER-2023
On July 30, 2023 several Curve Finance native-ETH stable pools were exploited via a compiler/toolchain supply-chain bug in specific Vyper versions (0.2.15, 0.2.16, 0.3.0). The compiler's storage-slot allocator assigned every @nonreentrant(key) decorator its own unique storage slot instead of reusing one shared slot per key, so functions meant to share a single reentrancy lock each got an independent, separately-set lock. This left the guard effective against single-function reentrancy but defeated cross-function reentrancy, letting an attacker re-enter a different guarded function via the native-ETH transfer callback while balances were mid-update. WETH-paired pools were unaffected; the exploited native-ETH pools included CRV/ETH, pETH/ETH, msETH/ETH and alETH/ETH, impacting Alchemix, JPEG'd and Metronome. Gross losses were around $61M; white-hat actors and MEV bots such as c0ffeebabe.eth returned a significant portion, reducing net losses to roughly $52M.
- CRITICALWEB3-CONIC-2023
On 21 July 2023 Conic Finance's ETH Omnipool on Ethereum lost roughly 1,700 ETH, about $3.6 million, to a read-only reentrancy attack. The attacker flash-loaned around $134 million, deposited into the Curve rETH pool, then called Curve's remove_liquidity(), which sends ETH to the recipient before the pool's totalSupply and balances are finalized, triggering the attacker contract's fallback during an inconsistent intermediate state. Inside that callback the attacker re-entered ConicEthPool.withdraw(), causing Conic's Curve LP oracle to value the LP token from Curve's virtual price and totalSupply while the pool was mid-operation, returning an inflated price. Conic's reentrancy guard was bypassed because its _isETH check assumed Curve v2 ETH pools list the native ETH placeholder address (0xEeee...EEeE) as a coin, whereas they actually use the WETH address, so the guard never fired. The inflated valuation let the attacker mint excess cncETH and withdraw more than deposited.
- CRITICALWEB3-EULER-2023
On March 13, 2023 Euler Finance, an Ethereum DeFi lending protocol, was drained of roughly $197M across DAI, wBTC, stETH and USDC. The root cause was a missing health check in the donateToReserves function, which let a user transfer eTokens to the protocol's reserves without any solvency verification. Funded by a ~$30M Aave flash loan, the attacker used Euler's leveraged minting (up to ~19x) to build a position of roughly 410M eDAI against 390M dDAI, then called donateToReserves to push the account into bad debt (insolvency) on purpose. They then self-liquidated through a second address; Euler's soft-liquidation logic applied a steep discount that grew with account unhealthiness, paying the liquidator far more collateral than the outstanding debt, which produced the profit after the flash loan was repaid. The attacker, identifying as 'Jacob', subsequently returned essentially all of the stolen funds, with Euler confirming full recovery in early April 2023.
- CRITICALWEB3-RARI-FEI-2022
On 30 April 2022 the Rari Capital / Fei Protocol Fuse lending pools on Ethereum lost approximately $80 million (about $79.7 million across ETH, FEI, DAI, LUSD and USDC). Fuse pools were a fork of Compound's CToken, but the CEther contract sent ETH using low-level call.value() instead of Compound's gas-capped transfer(), forwarding all remaining gas to the recipient's fallback. The borrow() function called doTransferOut(), which performed that call.value() ETH transfer to the borrower before the borrow and collateral accounting was finalized, violating checks-effects-interactions. The attacker's fallback re-entered the Comptroller's exitMarket() while the deposited collateral was still counted as backing the loan, freeing the collateral while keeping the borrowed ETH; the Comptroller's reentrancy guard did not cover exitMarket on the affected pools. Funded by Balancer flash loans, this cross-contract reentrancy drained seven pools.
- CRITICALWEB3-BEANSTALK-2022
On April 17, 2022, the Beanstalk stablecoin protocol was drained of about $182 million in a governance attack amplified by a flash loan, netting the attacker roughly $80 million after repaying the loan. The attacker borrowed about $1 billion across Aave and other venues (350M DAI, 500M USDC, 150M USDT plus BEAN and LUSD), deposited it into Curve to mint roughly 795M BEAN3CRV-f and 59M BEANLUSD-f LP tokens, and supplied them to Beanstalk's Silo to instantly hold a supermajority (over 78%, above the two-thirds threshold) of STALK governance power. Beanstalk's emergencyCommit path let a proposal pass once 24 hours had elapsed and a two-thirds vote existed; the attacker had pre-submitted a malicious BIP (BIP-18) whose init contract transferred the protocol's funds, then executed emergencyCommit in a single transaction. The core flaw was that voting power could be acquired flash-loan-instantly with no time-lock against single-block voting. Funds were laundered through Tornado Cash and never recovered; the attacker remains anonymous.