Résumé
On 19 April 2020 the dForce Lendf.me lending market on Ethereum lost roughly $25 million across more than ten supported assets. Lendf.me was a fork of Compound v1's MoneyMarket contract and accepted imBTC, an ERC-777 token whose ERC-20 transferFrom path silently fires the sender's tokensToSend hook (registered via the ERC-1820 registry) before balances settle. The supply() function cached the caller's current collateral into a local variable, then called doTransferIn() which invoked imBTC.transferFrom() before writing the updated balance to storage, violating checks-effects-interactions. The transfer fired the attacker's tokensToSend hook, which cross-function re-entered withdraw() to pull imBTC back out and correctly decrement stored collateral; control then returned to supply(), which overwrote storage with the stale cached value and erased the withdrawal. Each loop minted phantom collateral that was used to borrow and drain every pool, an ERC-777 cross-function reentrancy.
Comment l’éviter dans votre code
- Apply checks-effects-interactions: write updated balances to storage before any token transfer or external call
- Guard supply/withdraw and all state-changing entrypoints with a nonReentrant mutex covering cross-function paths
- Never cache balances in locals across an external call; re-read storage after the call returns
- Treat ERC-777/ERC-677/ERC-1363 transfer hooks (tokensToSend, tokensReceived) as untrusted reentry points
- Whitelist supported collateral tokens and reject assets with callback-bearing transfer semantics
Références
- https://peckshield.medium.com/uniswap-lendf-me-hacks-root-cause-and-loss-analysis-50f3263dcc09
- https://slowmist.medium.com/slowmist-details-of-lendf-me-reentrancy-attack-3e168ab5f2b1
- https://quantstamp.com/blog/how-the-dforce-hacker-used-reentrancy-to-steal-25-million
- https://medium.com/dforcenet/a-summary-of-the-attack-on-lendf-me-on-april-19-2020-e2f1c5d96640
Vulnérabilités liées
Tout 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 30 July 2023, several Curve Finance pools were drained of about $70 million, and the bug was not in Curve's code at all. It was in the compiler. Specific versions of Vyper, the programming language many Ethereum contracts are written in, generated a broken reentrancy guard, so the protection developers thought they had was silently not working. It is the rare and unsettling case of a reentrancy attack reaching contracts whose authors had correctly added the guard, because the tool that built them betrayed them. It put the spotlight on the compiler and the build pipeline as part of a smart contract's trust boundary.
- 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 13 March 2023 the Ethereum lending protocol Euler Finance was drained of about $197 million, the biggest DeFi hack of the year. The attacker did not steal a key or break any cryptography. They borrowed a fortune with a flash loan, used a single missing safety check in Euler's code to deliberately push their own position into bad debt, and then exploited Euler's own liquidation rules to be paid far more than they were owed. In a now-familiar twist, the attacker, identifying only as "Jacob," gave more than all of it back over the following weeks. It is a clean lesson in DeFi's defining risk: composable money where one unchecked code path can be turned into a money pump.
- 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.