Calculated from recorded token losses using historical USD prices at the incident time.
0x27dd6e51bf715cfc0e2fe96af26fc9ded89e4be8BSC0x0d5bae8f5232820ef56d98c04b8f531d2742555fBSCTwo BSC transactions, 0x6450d8f4db09972853e948bee44f2cb54b9df786dace774106cd28820e906789 at block 8530974 and 0xeaaa8f4d33b1035a790f0d7c4eb6e38db7d6d3b580e0bbc9ba39a9d6b80dd250 at block 8530987, exploited two ElevenNeverSellVault deployments in Eleven Finance. In each transaction, the attacker acquired LP, deposited it into the vault, called emergencyBurn() to pull the LP back without burning vault shares, and then called withdrawAll() to redeem the same shares a second time. The first transaction ended with 647573873780118425950729 BUSD credited to the attacker EOA, and the second ended with 30751016637331500259 BTCB credited to the same EOA.
The root cause is a code-level accounting bug in ElevenNeverSellVault: emergencyBurn() transfers the caller's LP entitlement back to the caller but never burns the vault shares that represent that entitlement. Because withdrawAll() simply redeems balanceOf(msg.sender), the unchanged shares can be cashed out immediately for a second LP withdrawal.
ElevenNeverSellVault is an LP vault that mints one vault share per unit of deposited underlying LP. Users enter through deposit() or , and the vault pushes LP into while tracking the user claim through share balances. The normal exit path is , which first burns from the caller and only then transfers the underlying LP back to the caller.
depositAll()MasterMindwithdraw(uint256 _shares)_sharesThat intended accounting matters because the vault share balance is the entire authorization mechanism for withdrawing LP. If a user can receive LP without consuming shares, the user can redeem the same economic claim twice. Both victim deployments exposed the same public methods and the same faulty emergencyBurn() implementation, so the exploit was permissionless and repeatable by any unprivileged actor able to source temporary LP liquidity.
The vulnerability is an accounting break in a public emergency exit path. deposit() mints shares 1:1 with deposited LP, and withdraw() correctly consumes those shares with _burn(msg.sender, _shares) before returning LP. emergencyBurn() copies the LP-withdrawal logic but omits the burn step entirely. That omission violates the invariant that each share can authorize at most one redemption of underlying LP.
The breakpoint is straightforward: after emergencyBurn() returns, the caller already holds the withdrawn LP again while balanceOf(msg.sender) is unchanged. The caller can then invoke withdrawAll(), which routes into withdraw(balanceOf(msg.sender)), burns the still-present shares, and transfers a second LP payout. The collected traces show exactly this sequence in both seed transactions, and the balance-diff artifacts show the resulting attacker profits in BUSD and BTCB after the LP is unwound and flash liquidity is repaid.
The relevant victim code is the contrast between withdraw() and emergencyBurn() in both vault instances:
function withdraw(uint256 _shares) public {
claim(msg.sender);
_burn(msg.sender, _shares);
uint avai = available();
if (avai < _shares) IMasterMind(mastermind).withdraw(nrvPid, (_shares.sub(avai)));
token.safeTransfer(msg.sender, _shares);
emit Withdrawn(msg.sender, _shares, block.number);
updateDebt(msg.sender);
}
function emergencyBurn() public {
uint balan = balanceOf(msg.sender);
uint avai = available();
if (avai < balan) IMasterMind(mastermind).withdraw(nrvPid, (balan.sub(avai)));
token.safeTransfer(msg.sender, balan);
emit Withdrawn(msg.sender, balan, block.number);
}
withdraw() proves the intended invariant: payout requires burning the shares first. emergencyBurn() computes the same balanceOf(msg.sender) and performs the same underlying transfer, but never calls _burn. That makes the caller's LP entitlement reusable.
The first seed trace shows the exact exploit path through helper contract 0x8b2979d88be70ba1b0c48691bdcae7909d084401 against vault 0x27dd6e51bf715cfc0e2fe96af26fc9ded89e4be8:
0x8b2979...::trigger()
0x27DD6E51...::depositAll()
0x27DD6E51...::emergencyBurn()
emit Withdrawn(user: 0x8b2979..., amount: 411515297079048812211459, block: 8530974)
0x27DD6E51...::withdrawAll()
emit Withdrawn(user: 0x8b2979..., amount: 411515297079048812211459, block: 8530974)
The duplicated Withdrawn amount is the proof that the same share position redeemed the same LP amount twice. The second seed trace shows the same pattern through helper 0x01eae4e246dfae08a4ef5c3d8f31d0668686eab3 against vault 0x0d5bae8f5232820ef56d98c04b8f531d2742555f, with duplicated Withdrawn events for 30836253259057738487 LP units in block 8530987.
After the second LP redemption, the attacker unwound the duplicated LP exposure back into profit tokens and repaid flash liquidity inside the same transaction. The balance-diff artifacts confirm that the attacker EOA 0xc71e2f581b77de945c8a7a191b0b238c81f11ed6 ended the first exploit with 647573873780118425950729 additional BUSD and the second with 30751016637331500259 additional BTCB, while only paying the recorded BNB gas costs.
The exploit sequence is end-to-end permissionless:
0xc71e2f581b77de945c8a7a191b0b238c81f11ed6 sent a transaction to an attacker-deployed helper contract.depositAll() on the vault.emergencyBurn(), which withdrew LP back to the helper but left the helper's vault shares unchanged.withdrawAll(), which burned those unchanged shares and transferred a second LP amount of equal size.The two exploit transactions differ only in the specific vault and profit token:
0x6450d8f4db09972853e948bee44f2cb54b9df786dace774106cd28820e906789 targeted the NRV/BUSD vault 0x27dd6e51bf715cfc0e2fe96af26fc9ded89e4be8 through helper 0x8b2979d88be70ba1b0c48691bdcae7909d084401.0xeaaa8f4d33b1035a790f0d7c4eb6e38db7d6d3b580e0bbc9ba39a9d6b80dd250 targeted the NRV/BTCB vault 0x0d5bae8f5232820ef56d98c04b8f531d2742555f through helper 0x01eae4e246dfae08a4ef5c3d8f31d0668686eab3.Because the vulnerable methods were public and no privileged state was required beyond the vault already holding user LP, this was an ACT opportunity rather than a privileged compromise.
The direct measurable losses captured in the collected balance diffs are:
647573873780118425950729 BUSD (18 decimals) from the first exploit transaction.30751016637331500259 BTCB (18 decimals) from the second exploit transaction.The underlying protocol loss was the duplicated withdrawal of LP assets from two separate ElevenNeverSellVault instances. Those duplicated LP withdrawals were converted into final profit tokens by the attacker, leaving the vaults and their depositors short the corresponding value.
0x27dd6e51bf715cfc0e2fe96af26fc9ded89e4be8 verified source showing withdraw() and emergencyBurn().0x0d5bae8f5232820ef56d98c04b8f531d2742555f verified source showing the same bug.0x6450d8f4db09972853e948bee44f2cb54b9df786dace774106cd28820e906789, which shows depositAll() -> emergencyBurn() -> withdrawAll() and duplicated Withdrawn events.0xeaaa8f4d33b1035a790f0d7c4eb6e38db7d6d3b580e0bbc9ba39a9d6b80dd250, which shows the same sequence against the second vault.