Eleven Finance ElevenNeverSellVault emergencyBurn() Missing Share Burn / Double-Withdrawal Drain (2021-06-22)
- Name: Eleven Finance
ElevenNeverSellVaultemergencyBurn()missing share burn / double-withdrawal drain - Date: 2021-06-22
- Protocol: Eleven Finance
- Chain: BNB Chain
- Category: duplicate claim / replayable redemption / burn omission / yield-vault accounting failure
- Loss: about 4.6M was drained across multiple vaults; later reporting said roughly 93.3% was recovered, leaving a net loss around $301.5k
- Summary: Starting around
2021-06-22 22:58:39 UTC, Eleven Finance’sElevenNeverSellVaultlet users exit LP positions throughemergencyBurn(), but the function transferred underlying LP tokens out without actually burning the user’s vault shares. That turned one vault position into a replayable redemption right. In the first attack leg, the attacker used a flashswap-assisted BUSD borrow, built an NRV-BUSD LP position, deposited411,515.29Pancake LP tokens into vault0x27DD6E51BF715cFc0e2fe96Af26fC9DED89e4BE8, calledemergencyBurn()to pull those same LP tokens back out while still keeping the11nrvBUSDshares, and then calledwithdrawAll()to redeem the unchanged share balance a second time. The vault thus paid the same position twice. - Trigger / entry point:
- Eleven exposed an
emergencyBurn()path onElevenNeverSellVaultfor pulling user funds out of the strategy stack. - That branch returned underlying LP tokens but failed to consume the corresponding user shares.
- The normal withdrawal branch still treated the unchanged share balance as valid redemption authority.
- Because the vault held real LP-backed value, reusing the same share claim immediately monetized into drainable assets.
- Eleven exposed an
- Root cause:
- Eleven violated the one-position-one-redemption invariant: after
emergencyBurn()finished, the user’s claim on vault assets should have been extinguished or reduced to zero. - Instead, the function transferred value first and left the accounting object that authorized later withdrawal untouched.
- This was not mainly a flash-loan pricing trick. The flash liquidity only made it cheap to build a large temporary LP position; the real bug was branch-specific consumed-state omission.
- In pattern terms,
emergencyBurn()created a cross-branch replay: one exit path paid out, but another exit path still believed the same shares had never been spent.
- Eleven violated the one-position-one-redemption invariant: after
- Exploit path:
- The attacker funded the operation from address
0xc71e2f581b77de945c8a7a191b0b238c81f11ed6and used a flashswap against the ApeSwap pair at0x51e6D27FA57373d8d4C256231241053a70Cb1d93to borrow953,869.62BUSD. - They swapped
340,631.23BUSD for474,378.75NRV. - They added
474,378.75NRV plus366,962.02BUSD into the NRV-BUSD Pancake pool and minted411,515.29LP tokens. - They deposited those LP tokens into Eleven’s
ElevenNeverSellVault, receiving411,515.29vault shares (11nrvBUSD). - They called
emergencyBurn(), which returned the underlying LP tokens but did not burn or decrement the user’s vault-share balance. - With the same
11nrvBUSDshares still live, they immediately calledwithdrawAll()and redeemed a second copy of the same vault position. - They removed liquidity, swapped the recovered NRV back into BUSD, repaid the flash loan plus fee, and kept the surplus.
- The attacker repeated the same primitive across at least four
ElevenNeverSellVaultinstances via additional attack contracts, draining multiple vaults before the protocol could halt the damage.
- The attacker funded the operation from address
- What the attacker needed to be true:
emergencyBurn()had to transfer or unwrap real vault value without clearing the user’s share entitlement.- The ordinary withdrawal path had to trust raw share balance as sufficient redemption authority, with no additional
already emergency exitedguard. - The vaults had to hold enough redeemable LP-backed assets for the duplicate withdrawal to matter economically.
- Flash liquidity had to be available so the attacker could scale the initial LP deposit quickly and atomically.
- Technical anatomy worth remembering:
- This is the yield-vault version of duplicate-claim failure: the replayed object was not a Merkle proof or lock ID, but a share balance that should have been consumed on the first exit path.
- The crucial bug was asymmetric branch behavior.
emergencyBurn()andwithdrawAll()were meant to be alternate ways to exit one position, but only one of them updated consumed state. - The exploit path was tiny at the contract-call level — effectively
depositAll(); emergencyBurn(); withdrawAll();after preparing the LP position — which is exactly why branch-convergence review matters more than surface-level strategy complexity. - The exploit also illustrates why
emergencypaths deserve especially hostile review. Teams often relax invariants to make exits easier under stress, but that branch still has to preserve the economic identity of redeemed claims. - Flash liquidity amplified the attack, but the vault would still have been logically broken without it. The core failure was
assets moved outno longer implyingshares moved down.
- Representative transactions and contracts:
- Main exploit transaction:
0x6450d8f4db09972853e948bee44f2cb54b9df786dace774106cd28820e906789 - One later exploit transaction cited by public exploit catalogs:
0xeaaa8f4d33b1035a790f0d7c4eb6e38db7d6d3b580e0bbc9ba39a9d6b80dd250 - Primary attacker address:
0xc71e2f581b77de945c8a7a191b0b238c81f11ed6 - Vulnerable
ElevenNeverSellVaultinstance from the first exploit leg:0x27DD6E51BF715cFc0e2fe96Af26fC9DED89e4BE8 - Flashswap pair used in one public proof of concept:
0x51e6D27FA57373d8d4C256231241053a70Cb1d93 - NRV-BUSD Pancake LP token used in the first exploit leg:
0x401479091d0F7b8AE437Ee8B054575cd33ea72Bd
- Main exploit transaction:
- Response / mitigation:
- Eleven Finance paused affected vaults after the exploit and investigated the vault logic failure.
- Public postmortem coverage converged on the missing-share-burn bug in
emergencyBurn()as the decisive root cause, with PeckShield describing the attack as a flashswap-assisted drain that hit at least fourElevenNeverSellVaultpools. - Later exploit-catalog reporting said most funds were recovered, though the incident still remains a strong accounting-path failure exemplar.
Pattern tags
- duplicate-claim-idempotency-failure
- issuance-redemption-solvency-asymmetry
emergencyBurn()paid underlying LP value without consuming the vault-share entitlement that still authorizedwithdrawAll()- The exploitable state transition was branch-specific:
depositAll()minted11nrvBUSD,emergencyBurn()returned the backing LP, and only the later ordinary withdrawal branch actually burned the shares - Flashswap liquidity only scaled the temporary LP position; it did not create the accounting bug that made one share balance redeemable twice
- The same primitive repeated cleanly across multiple
ElevenNeverSellVaultinstances, which is exactly what you expect when the bug isexit-right consumed state missing on one branchrather than pool-specific pricing distortion
Why the exploit worked
- PeckShield’s RCA makes the decisive predicate blunt:
emergencyBurn()was intended to let the user withdraw funds and burn shares, but after transferring the underlying LP tokens it never actually burned the user’s11nrvBUSDbalance. - The exploit therefore did not need to trick a price oracle or skew vault exchange rate math. The attacker only had to create a large enough LP position, deposit it once, and then exercise two exit branches against the same entitlement object.
- The public proof-of-concept captures the exploit surface almost embarrassingly well: after preparing the LP tokens, the core path is effectively
vault.depositAll(); vault.emergencyBurn(); vault.withdrawAll();. That is useful corpus material because it shows how little strategy complexity is required once one branch stops consuming the right that another branch still honors. - This is also a strong
emergency path is production codecase. The bug lived in the escape hatch intended to help users exit stressed positions; the ordinary withdrawal path stayed economically live and honest enough to cash the stale share balance out a second time. - The flashswap mattered only because it let the attacker rent a large temporary position atomically. The vault would still have been structurally broken without flash liquidity, because
assets transferred outhad stopped implyingshares extinguished.
Detection and audit pivots
- Test economic equivalence across all exit verbs for the same position:
withdraw,withdrawAll,emergencyBurn, migration helpers, rescue paths, and any router wrapper should all consume the same share entitlement object. - Property-test the minimal replay sequence directly: after
deposit(x), any one successful exit path should make every sibling exit path worth zero incremental value for that same shares balance. - Track post-exit supply invariants, not just asset movement. If a vault returns LP tokens but total shares and user shares do not fall by the redeemed amount, the protocol has already created replayable withdrawal authority.
- Review
emergencybranches with the same hostility as ordinary redemptions. Faster or simpler exit logic is not allowed to fork consumed-state semantics from the main withdrawal path. - Ask whether flash liquidity is only capital amplification. If the same exploit would still work with slower self-funded deposits, the real issue is usually accounting-path replayability rather than price manipulation.
Failure-prevention checklist
- Force every exit verb that can realize one vault position —
withdraw,withdrawAll,emergencyBurn, migration helpers, and rescue paths — through one shared entitlement-consumption primitive soassets outandshares downcannot diverge by branch. - Assert supply conservation on every redemption path. If an exit returns backing LP tokens, the corresponding user share balance and total vault share supply should fall in the same state transition.
- Property-test the exact replay sequence from the public proof of concept:
depositAll(); emergencyBurn(); withdrawAll();should leave the second exit worth zero additional value. - Treat
emergencylogic as full-production accounting code, not as a lighter operational shortcut. Escape hatches should simplify routing, not fork the consumed-state model. - Make sibling exit paths economically equivalent even when they use different helper flows or unwind strategies. Alternate redemptions may differ operationally, but they must not differ on which claim object is consumed.
- Reconcile outstanding share supply against redeemable backing after every exit branch, including paused-mode, migration-mode, or strategy-rescue paths.
- Fuzz branch orderings and mixed exit attempts for the same position in one transaction and across repeated calls. Reviewers should expect attackers to exercise
normal -> emergency,emergency -> normal, and helper-wrapped variants until some branch forgets to burn. - Keep incident monitoring focused on
value left the vault while shares stayed liverather than only on price anomalies. Duplicate-withdrawal vault bugs can look economically calm right until reserves are gone.
Audit prompts
- What exact storage object represents
this vault position has already been redeemed, and do all exit verbs consume that same object? - If a user exits through
emergencyBurn(), what precise state transition makes a laterwithdrawAll()economically inert for the same share balance? - Can any branch transfer LP tokens or underlying assets before user shares and total supply are reduced, and if so what stops replay?
- Are emergency, migration, or rescue paths reusing the main redemption primitive, or did they fork their own share-burning semantics?
- Does testing assert branch equivalence at the economic level, not just branch-local correctness? For one position, do
withdraw,withdrawAll, andemergencyBurnall reduce claimable value by the same amount? - If the vault unwinds strategy positions differently across exit modes, where is the invariant that the same claim cannot survive two different unwind routes?
- Could a flash loan, flash swap, or temporary self-funded deposit merely amplify a bug that would still exist at smaller scale, and are reviewers explicitly separating amplification from root cause?
- After any redemption, can a monitor or invariant check detect
backing returned but shares unchangedbefore the next withdrawal branch cashes out again?
Lessons that should already be standard
- An emergency exit is still an exit. If it pays real value, it has to consume the same entitlement object as the ordinary withdrawal path.
Assets transferred outis not enough; for vault systems, the real invariant isassets out implies shares downin the same redemption state machine.- Branch-local correctness is a trap. The right review question is whether all sibling paths that honor one position remain economically equivalent after any one of them executes.
- Flash liquidity often only rents attack size. When the exploit core is
deposit -> exit path A -> exit path B, the real bug is replayable redemption authority. - Escape-hatch code is exactly where teams are most likely to relax invariants under operational pressure. That is why those branches deserve more hostility, not less.
- A share balance that survives one successful redemption is already a printing press, even if the first payout path was intended only for emergencies.
Why this incident matters
- It preserves an important subfamily where a vault does not need an oracle bug or reentrancy window to fail; it only needs one exit path that pays out without consuming the right being redeemed.
- It is a clean example of how
emergencyescape hatches can silently become duplicate-withdrawal branches if they are not forced back into the same share-burning invariant as ordinary exits. - It also broadens the duplicate-claim corpus beyond batched Merkle claims and lock unlocks into vault-share redemption logic.
- It is a particularly reusable review case because the exploit path is so compact. When
deposit -> emergency exit -> ordinary exitworks in one transaction, the system is advertising that its economic rights are defined by branch behavior rather than by one canonical consumed-state object.
Internal linkages
- Core recurring pattern: duplicate-claim-idempotency-failure.
- Adjacent accounting frame: issuance-redemption-solvency-asymmetry — useful when the sharper question is how one branch created a still-redeemable claim while another branch kept paying out against real LP-backed reserves.
- Positive-design companion: exit-right-consumed-state-binding — the healthiest follow-up when reviewing how
emergencyBurn()and ordinary withdrawals should have been forced to consume one shared vault-exit entitlement. - Useful contrast: entrypoint-equivalence-under-shared-standards — the more general design lesson is that sibling entrypoints claiming to do the same economic job cannot be allowed to drift on which invariant they actually preserve.
Control surface
-
The leverage sat in the
ElevenNeverSellVaultexit branches, especially whetheremergencyBurn()andwithdrawAll()both consumed the same share entitlement or only one of them did. -
Eleven did not primarily fail because LP pricing moved or because flash loans existed. It failed because a redeemed vault share remained live after value had already been paid out once.
-
The most useful control-surface framing is
where does this vault make second exercise worth zero?In Eleven’s case, that answer differed by branch, which is exactly why one position could be cashed twice. -
Sources:
-
Last reviewed: 2026-07-24 UTC