Bearn Dust Sweep Exploit
Exploit Transactions
0x51913be3f31d5ddbfc77da789e5f9653ed6b219a52772309802226445a1edd5fVictim Addresses
0x21125d94cfe886e7179c8d2fe8c1ea8d57c73e0eBSCLoss Breakdown
Similar Incidents
Elephant Treasury Sweep Sandwich
44%Channels Dust-Share Drain
37%SwapX Stale-Allowance Drain
33%CCV Treasury Rebalancer Attack
31%Allbridge Pool Mispricing
31%CS Pair Balance Burn Drain
31%Root Cause Analysis
Bearn Dust Sweep Exploit
1. Incident Overview TL;DR
In BNB Chain block 34099689, exploit transaction 0x51913be3f31d5ddbfc77da789e5f9653ed6b219a52772309802226445a1edd5f used a flash-borrowed 10,000 WBNB position to manipulate the ALPACA/WBNB spot price, then forced Bearn.fi strategy 0x21125d94cfe886e7179c8d2fe8c1ea8d57c73e0e to execute convertDustToEarned(). The strategy spent its full 768358953227757555088466 BUSD dust balance and received only 513348353791467415682 ALPACA at the manipulated price. The attacker EOA finished with 760889017097225537647455 BUSD gross output, and exact fee pricing at block 34099688 shows 34398477869744779784 BUSD of cluster costs, leaving a deterministic net gain of 760854618619355792867671 BUSD.
The root cause is a permissionless maintenance path that moved treasury value through manipulable AMM spot liquidity with no slippage floor. earn() was gated by notPublic || isAuthorised(msg.sender), but convertDustToEarned() remained public and executed swapExactTokensForTokensSupportingFeeOnTransferTokens(..., 0, ...) against PancakeSwap.
2. Key Background
Bearn.fi BvaultsStrategy compounds by converting a residual wantAddress balance into earnedAddress along a preconfigured PancakeSwap path. For this strategy instance, wantAddress was BUSD, earnedAddress was ALPACA, and the configured path at the exploit pre-state was BUSD -> WBNB -> ALPACA. At block 34099688, the strategy was unpaused, isAutoComp was true, and the contract held 768358953227757555088466 BUSD and 0 ALPACA.
PancakeSwap v2 prices are the instantaneous reserves of each pair. When a caller is allowed to trigger a treasury swap against those reserves with amountOutMin=0, a flash-borrow can move the spot price, insert the victim swap into the distorted state, and then unwind before the transaction ends.
The verified victim source shows the critical mismatch between the authorized compound entrypoint and the public dust-sweep entrypoint:
function earn() public whenNotPaused {
require(isAutoComp, "BvaultsStrategy: !isAutoComp");
require(!notPublic || isAuthorised(msg.sender), "BvaultsStrategy: !authorised");
...
}
function convertDustToEarned() public whenNotPaused {
require(isAutoComp, "!isAutoComp");
uint256 wantAmt = IERC20(wantAddress).balanceOf(address(this));
if (wantAddress != earnedAddress && wantAmt > 0) {
IERC20(wantAddress).safeIncreaseAllowance(uniRouterAddress, wantAmt);
IPancakeRouter02(uniRouterAddress)
.swapExactTokensForTokensSupportingFeeOnTransferTokens(
wantAmt, 0, paths[wantAddress][earnedAddress], address(this), now + 60
);
}
}
3. Vulnerability Analysis & Root Cause Summary
The vulnerability is an externally triggerable treasury swap executed against raw AMM spot liquidity with zero minimum output. The contract correctly recognized that its main compounding path needed optional authorization through notPublic || isAuthorised(msg.sender), but it left convertDustToEarned() callable by any address while using the same routing model. That function did not cap slippage, consult a TWAP or oracle, or restrict the execution moment. Because the strategy held a large BUSD dust balance at the exploit pre-state, the public call had enough notional size to turn a temporary ALPACA/WBNB price distortion into material value transfer. The safety invariant was straightforward: converting residual strategy inventory into earned tokens should preserve value within bounded slippage or remain restricted to trusted operators. The concrete breakpoint was the public router call swapExactTokensForTokensSupportingFeeOnTransferTokens(wantAmt, 0, paths[wantAddress][earnedAddress], address(this), now + 60) inside convertDustToEarned().
4. Detailed Root Cause Analysis
Immediately before the attack, the strategy was in the exact state needed for exploitation: it was unpaused, isAutoComp was true, and it held a large BUSD dust balance that convertDustToEarned() would consume in one shot. The attacker exploited that public reachability by first borrowing 10,000 WBNB from Pancake pair 0x0ed7e52944161450477ee417de9cd3a859b14fd0, then swapping it into ALPACA on pair 0xf3ce6aac24980e6b657926dfc79502ae414d3083. That moved the ALPACA/WBNB reserves from 224654220879620693092492 ALPACA / 184266460067981786400 WBNB to 4072722629168589855796 ALPACA / 10184266460067981786400 WBNB, sharply worsening the strategy's buy price.
The exploit trace shows the manipulated victim leg and the profit realization in sequence:
PancakeRouter::swapExactTokensForTokensSupportingFeeOnTransferTokens(
10000000000000000000000, 0, [WBNB, ALPACA], attackerContract, ...
)
0x21125d94Cfe886e7179c8D2fE8c1EA8D57C73E0e::convertDustToEarned()
PancakeRouter::swapExactTokensForTokensSupportingFeeOnTransferTokens(
768358953227757555088466, 0, [BUSD, WBNB, ALPACA], strategy, ...
)
ALPACA::transfer(strategy, 513348353791467415682)
...
BEP20Token::transfer(attackerEOA, 760889017097225537647455)
WBNB::transfer(flashPair, 10025062656641604020000)
The victim was therefore not drained by a privileged withdrawal path or by a bug in share accounting. Instead, the attacker forced the strategy to make a valid but catastrophically mispriced market buy. The BUSD balance diff confirms the strategy's BUSD balance fell from 768358953227757555088466 to 0, while the ALPACA balance diff confirms the strategy only received 513348353791467415682 ALPACA.
The revised fee-pricing artifact also makes the ACT predicate fully deterministic. At block 34099688, PancakeRouter getAmountsOut on the WBNB -> BUSD path priced the helper's 0.1 WBNB top-up at 23039541496434381003 BUSD and the exploit transaction's 0.0493047 BNB gas cost at 11359794282019701109 BUSD:
{
"helper_top_up_busd": "23039541496434381003",
"gas_cost_busd": "11359794282019701109",
"total_cluster_cost_busd": "34398477869744779784",
"net_cluster_value_delta_busd": "760854618619355792867671"
}
That pricing closes the only prior validation gap. The exploit remained permissionless, reproducible from public state, and profitable after exact cost accounting.
5. Adversary Flow Analysis
- The attacker EOA
0xce27b195fa6de27081a86b98b64f77f5fb328dd5called exploit contract0xe1997bc971d5986aa57ee8ffb57eb1deba4fdaaawith helper address0x1ccc8ee8ad0f70e0bb362d56035ff241755192b1. - The exploit contract flash-borrowed
10,000 WBNBfrom Pancake pair0x0ed7e52944161450477ee417de9cd3a859b14fd0. - The borrowed WBNB was swapped into ALPACA, inflating the ALPACA/WBNB price before the victim traded.
- The exploit contract called
BvaultsStrategy::convertDustToEarned(), forcing strategy0x21125d94cfe886e7179c8d2fe8c1ea8d57c73e0eto sell all768358953227757555088466BUSD dust through the manipulatedBUSD -> WBNB -> ALPACAroute. - After the strategy overpaid for ALPACA, the attacker sold ALPACA back into WBNB, then swapped
1445602616404598650387WBNB into760889017097225537647455BUSD for the attacker EOA. - The helper address transferred
0.1 WBNBinto the exploit contract to close the flash-swap fee gap, and the exploit repaid10025062656641604020000WBNB to the flash-lending pair.
This sequence is a textbook same-transaction spot-manipulation exploit: the attacker controlled the market state immediately before the victim swap and monetized the victim's forced trade immediately after it.
6. Impact & Losses
The impacted component was Bearn.fi strategy 0x21125d94cfe886e7179c8d2fe8c1ea8d57c73e0e. The strategy lost the full BUSD dust inventory that made the maintenance path valuable, and it was left holding only 513348353791467415682 ALPACA bought at the manipulated price.
Measured impact:
- Victim BUSD spent:
768358953227757555088466 - Gross BUSD received by attacker EOA:
760889017097225537647455 - Exact adversary-cluster costs priced in BUSD:
34398477869744779784 - Exact net adversary-cluster value delta:
760854618619355792867671
For metadata and loss tracking, the primary protocol loss is the 760889017097225537647455 BUSD extracted to the attacker EOA.
7. References
- Exploit transaction:
0x51913be3f31d5ddbfc77da789e5f9653ed6b219a52772309802226445a1edd5fin BNB Chain block34099689 - Verified victim contract: Bearn.fi
BvaultsStrategyat0x21125d94cfe886e7179c8d2fe8c1ea8d57c73e0e - Flash-lending pair:
0x0ed7e52944161450477ee417de9cd3a859b14fd0 - Manipulated ALPACA/WBNB pair:
0xf3ce6aac24980e6b657926dfc79502ae414d3083 - Seed trace artifact:
/workspace/session/artifacts/collector/seed/56/0x51913be3f31d5ddbfc77da789e5f9653ed6b219a52772309802226445a1edd5f/trace.cast.log - Seed balance-diff artifact:
/workspace/session/artifacts/collector/seed/56/0x51913be3f31d5ddbfc77da789e5f9653ed6b219a52772309802226445a1edd5f/balance_diff.json - Exploit metadata artifact:
/workspace/session/artifacts/collector/seed/56/0x51913be3f31d5ddbfc77da789e5f9653ed6b219a52772309802226445a1edd5f/metadata.json - Fee-pricing artifact:
/workspace/session/artifacts/auditor/iter_1/fee_pricing.json