# EarningFram ENF_ETHLEV: reentrancy drains leveraged vault

Reentrancy in EarningFram's ETH leveraged vault let attacker deposit then re-enter withdraw during ETH callback, burning shares cheaply and draining ~$286k.

- Payout: Real-world hack
- Severity: high
- Program: EarningFram
- Analysis by: DeFiHackLabs
- Disclosed: 2023-08-09
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2023-08/EarningFram_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20230809-earningfram
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained vuln:reentrancy

**Target:** ENF_ETHLEV leveraged ETH vault (ERC4626-style deposit/withdraw)
**Root cause:** Vault sends ETH to receiver during withdraw before finalizing share accounting, allowing re-entry via the receive() callback to manipulate share/asset conversion and burn fewer shares than assets withdrawn.
**Steps:**
1. Flash-borrow `10_000 ether` WETH from Uniswap V3 pair `0x88e6...5640`.
2. Unwrap WETH to ETH via `WETH.withdraw`.
3. Approve vault and call `ENF_ETHLEV.deposit{value: assets}(assets, address(this))` to mint shares.
4. Call `ENF_ETHLEV.withdraw(convertToAssets(balanceOf(this)), address(this))`; during ETH transfer the `receive()` callback fires.
5. In `receive()` (msg.sender == Controller) transfer remaining shares minus 1 to the Exploiter contract, incrementing nonce.
6. Exploiter.withdraw() redeems its shares for ETH and forwards to attacker.
7. Rewrap ETH to WETH, repay flash loan `amount1 + amount`.
8. Loop while `ENF_ETHLEV.totalAssets() > 1 ether`.
**Exploitation:** Attacker flash-borrows 10k WETH, deposits full vault assets to mint shares, then withdraws; the ETH payout triggers receive() which re-enters and moves shares to a helper contract that redeems again, extracting more ETH than deposited. Repeated in a loop until vault drained (~$286k), then repays flash loan.
**Escalation:** Looping the flash-loan + reentrancy cycle drains the entire vault balance rather than a single withdrawal.
**Insight:** ERC4626-style vaults that pay out native ETH (or call arbitrary receivers) before updating share balances are reentrancy-prone; watch for `receive()`/callback hooks firing mid-withdraw and check whether share burn happens before or after the external transfer.

