All incidents

LaEeb Dead-Buyback Counter Reuse

Share
Oct 30, 2023 10:42 UTCAttackLoss: 3.5 BNBPending manual check1 exploit txWindow: Atomic
Estimated Impact
3.5 BNB
Label
Attack
Exploit Tx
1
Addresses
2
Attack Window
Atomic
Oct 30, 2023 10:42 UTC → Oct 30, 2023 10:42 UTC

Exploit Transactions

TX 1BSC
0x0d13a61e9dc81cfae324d3d80e49830d9bbae300f760e016a15600889a896a1b
Oct 30, 2023 10:42 UTCExplorer

Victim Addresses

0xa2b8a15a07385ea933088c6bcbb38b84c1051a58BSC
0x3921e8cb14e2c08db989fdf88d01220a0c53cc91BSC

Loss Breakdown

3.5BNB

Similar Incidents

Root Cause Analysis

LaEeb Dead-Buyback Counter Reuse

1. Incident Overview TL;DR

On BSC transaction 0x0d13a61e9dc81cfae324d3d80e49830d9bbae300f760e016a15600889a896a1b at block 33053188, an unprivileged attacker used a public DODO WBNB flash loan to buy LaEeb, repeatedly transferred LaEeb directly into the LaEeb/WBNB Pancake pair at 0x3921e8cb14e2c08db989fdf88d01220a0c53cc91, and immediately recovered the same tokens with PancakePair::skim. Those pseudo-sells repeatedly exercised LaEeb's sell-side maintenance logic until the LaEeb token contract at 0xa2b8a15a07385ea933088c6bcbb38b84c1051a58 accumulated enough native BNB to execute a 4 BNB dead-wallet buyback. The attacker then dumped LaEeb back into the pair, repaid the flash loan, and realized a net sender-level gain of 218451604933465601 wei in BNB/WBNB after gas.

The root cause is a logic bug in LaEeb's sell-maintenance path. AmountCountDeadBNB is incremented when dead-fee tokens are converted to BNB, but it is never decremented after LaEeb spends 4 BNB on the dead-wallet buyback. Because LaEeb also treats any plain token transfer to the pair as a sell, and PancakePair lets a caller reclaim excess pair balances with skim, an unprivileged attacker can repeatedly trigger the buyback path without surrendering the triggering inventory.

2. Key Background

LaEeb is a fee-on-transfer token that routes sell-side fees through internal maintenance functions before finalizing a user transfer whenever the recipient is marked as an automated market maker pair. The relevant pair is the LaEeb/WBNB Pancake pair at 0x3921e8cb14e2c08db989fdf88d01220a0c53cc91.

Two system behaviors make the exploit possible:

  1. PancakePair reserves are not updated by a plain ERC20 transfer. A direct transfer into the pair increases token balance but leaves reserve0 and reserve1 unchanged until swap, mint, burn, or sync.
  2. LaEeb's sell-side maintenance path is keyed only on automatedMarketMakerPairs[to], so a plain transfer into the pair executes the same maintenance logic that a real AMM sale would execute.

At the exploitable pre-state, block 33053187, the public values were already primed in the attacker's favor:

  • AmountCountDeadBNB = 78558676974458564672
  • AmountDeadBNB = 4000000000000000000
  • LaEeb native treasury balance = 3572802069843452599

That means the stale counter was already above the 4 BNB threshold, while the treasury still needed to be pushed above 4 BNB for the buyback branch to fire.

3. Vulnerability Analysis & Root Cause Summary

This incident is an ATTACK case caused by broken budget accounting inside LaEeb. The token tracks BNB generated from dead-fee conversions in AmountCountDeadBNB, but it does not consume that budget when it later spends 4 BNB on swapETHForTokensToAddress(..., deadWallet). As a result, the same recorded budget can authorize repeated dead-wallet buybacks.

The exploit is reachable because LaEeb equates any transfer into the pair with a sell. That lets an attacker trigger the sell-maintenance path using a plain token transfer instead of an irrevocable router sale. The paired PancakePair contract then returns the excess transferred tokens through skim, so the attacker can recycle nearly the same LaEeb inventory across many trigger iterations.

The safety invariant is straightforward: a counter representing accumulated buyback budget must decrease when the budget is spent, and treasury-spending logic should only run on transfers that correspond to a real swap input. LaEeb violates both conditions. The concrete breakpoint is the _transfer branch that spends AmountDeadBNB without reducing AmountCountDeadBNB.

4. Detailed Root Cause Analysis

The verified LaEeb source shows the sell-maintenance branch in _transfer:

if (
    canSwap &&
    !swapping &&
    automatedMarketMakerPairs[to] &&
    from != owner() &&
    to != owner() &&
    bbswapAndLiquifyEnabled &&
    from != address(this)
) {
    swapping = true;
    if (address(this).balance >= AmountDeadBNB && AmountCountDeadBNB >= AmountDeadBNB) {
        swapETHForTokensToAddress(AmountDeadBNB, deadWallet);
    }
    if (AmountDeadFee >= (100 * (10**18)) && balanceOf(address(this)) >= AmountDeadFee) {
        swapTokensForEthXH(AmountDeadFee);
    }
    ...
    swapping = false;
}

The verified helper that accumulates the budget is separate:

function swapTokensForEthXH(uint256 tokenAmount) private {
    uint256 initialBalance = address(this).balance;
    swapTokensForEth(tokenAmount);
    uint256 newBalance = address(this).balance.sub(initialBalance);
    AmountCountDeadBNB = AmountCountDeadBNB.add(newBalance);
    AmountDeadFee = AmountDeadFee.sub(tokenAmount);
}

The missing operation is the bug: after the buyback spends 4 BNB, LaEeb never subtracts 4 BNB from AmountCountDeadBNB. Once the counter exceeds the threshold, it remains reusable.

The verified PancakePair source supplies the second half of the exploit:

function skim(address to) external lock {
    address _token0 = token0;
    address _token1 = token1;
    _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
    _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}

Because a plain LaEeb transfer into the pair does not update reserves, skim returns the excess LaEeb immediately. The attacker can therefore:

  1. Buy LaEeb with public flash-loaned WBNB.
  2. Transfer LaEeb directly into the pair, which LaEeb treats as a sell.
  3. Let LaEeb run fee-conversion maintenance and increase treasury BNB.
  4. Call skim to recover the excess LaEeb from the pair.
  5. Repeat until the treasury native balance exceeds 4 BNB.
  6. Trigger one more transfer so LaEeb executes the 4 BNB dead-wallet buyback.
  7. Sell the retained LaEeb inventory back to WBNB and repay the flash loan.

The seed trace confirms that exact sequence. It shows repeated alternation between direct LaEeb-to-pair transfers and PancakePair::skim(0x0c96d5...), then the treasury-spending call:

PancakePair::skim(0x0c96d5A27f20a838b778caD29e111cbAeF85923a)
...
PancakeRouter::swapExactETHForTokensSupportingFeeOnTransferTokens{value: 4000000000000000000}(
  0,
  [WBNB, LaEeb],
  0x000000000000000000000000000000000000dEaD,
  ...
)

The public pre-state snapshot explains why the loop only needed to top up treasury BNB rather than prime the counter from zero. Before the incident transaction, AmountCountDeadBNB was already 78558676974458564672, while the treasury held only 3572802069843452599 wei. The attack therefore consumed an already stale authorization.

5. Adversary Flow Analysis

The adversary cluster consists of:

  • Sender EOA 0x7cb74265e3e2d2b707122bf45aea66137c6c8891
  • Orchestrator 0x9180981034364f683ea25bcce0cff5e03a595bef
  • Helper 0x0c96d5a27f20a838b778cad29e111cbaef85923a

The trace-backed flow is:

  1. The sender calls the orchestrator in block 33053188, tx index 0.
  2. The orchestrator borrows 8672889958749180735 WBNB from DODO pool 0xfeafe253802b77456b4627f8c2306a9cebb5d681.
  3. The helper swaps the borrowed WBNB for LaEeb through PancakeRouter and receives 72361332796676237007449844673667 LaEeb units after buy fees.
  4. The helper repeatedly transfers LaEeb directly to the pair and immediately calls skim, forcing LaEeb's sell-maintenance functions to run while recovering the transferred inventory.
  5. Once the LaEeb treasury balance exceeds 4 BNB, another trigger transfer causes LaEeb to execute a 4 BNB dead-wallet buyback.
  6. The helper sells 69524768551046528516757810762566 LaEeb units back to WBNB for 10416184159661589796 WBNB.
  7. The orchestrator repays the flash-loan principal and forwards the residual 1743294200912409061 WBNB to the sender.

The final profit derivation is deterministic. The sender started with:

  • Native BNB: 154753246901757699023
  • WBNB: 2218475368782335436
  • Total BNB/WBNB reference value: 156971722270540034459

After the transaction, the sender held:

  • Native BNB: 153228404305778755563
  • WBNB: 3961769569694744497
  • Total BNB/WBNB reference value: 157190173875473500060

The net sender-level reference-asset gain was therefore 218451604933465601 wei after paying 1524842595978943460 wei in gas. The helper and orchestrator return to their pre-state WBNB balances, so this sender-level delta equals the adversary-cluster delta.

6. Impact & Losses

The directly measured victim-side loss in the incident transaction is native BNB drained from the LaEeb token contract:

  • BNB loss: 3502602582368107367 wei

The balance diff also shows a corresponding dead-wallet token inflow:

  • Dead wallet gained 15405738501967291893024884551459 LaEeb units

From the attacker's perspective, the economically relevant result is the net increase in sender reference-asset value:

  • Net BNB/WBNB gain after gas: 218451604933465601 wei

The loss is localized to the LaEeb treasury and the forced dead-wallet purchase path. No privileged access, private keys, or attacker-only infrastructure were required; the opportunity was fully permissionless.

7. References

  1. BSC transaction 0x0d13a61e9dc81cfae324d3d80e49830d9bbae300f760e016a15600889a896a1b
  2. Verified LaEeb token source at 0xa2b8a15a07385ea933088c6bcbb38b84c1051a58
  3. Verified PancakePair source at 0x3921e8cb14e2c08db989fdf88d01220a0c53cc91
  4. Seed execution trace for the incident transaction
  5. Seed balance diff for the incident transaction
  6. Pre-state snapshot at block 33053187
  7. Auditor profit snapshot for the sender's BNB/WBNB before-and-after valuation