BBOX Pair Burn Price Manipulation
Exploit Transactions
0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3Victim Addresses
0x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8cBSC0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348BSCLoss Breakdown
Similar Incidents
CS Pair Balance Burn Drain
45%HEALTH Zero-Transfer Price Manipulation
44%ZS Pair Burn Drain
44%APE2 Pair Burn Exploit
40%UN Burn-Skim Exploit
40%ShadowFi Public Burn Drains WBNB Pair
40%Root Cause Analysis
BBOX Pair Burn Price Manipulation
1. Incident Overview TL;DR
On BSC block 23106507, transaction 0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3 exploited BBOXToken at 0x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8c and its Pancake pair 0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348. The attacker borrowed 3043124720737281210661 wei of WBNB from the DODO pool 0x0fe261aee0d1c4dfddee4102e82dd425999065f4, bought BBOX, routed the purchased BBOX through an attacker-controlled helper contract, and sold back into the manipulated pool.
The root cause is a token-side accounting bug inside BBOXToken._transfer. When pairAmountChange is enabled, any transfer whose sender is not marked as an AMM pair can burn pairAmount directly from uniswapV2Pair and immediately call sync(). That lets an unprivileged attacker reduce pair-held BBOX without paying the corresponding WBNB cost, distort the AMM price, and extract WBNB. Collector balance diffs and receipt logs show a final WBNB profit transfer of 38225164664108721893 wei to the attacker profit sink, while the attacker EOA paid 3838390000000000 wei in gas.
2. Key Background
BBOXToken is a fee-on-transfer token that tracks an accumulator named pairAmount. On buys and sells involving the Pancake pair, the token increments this accumulator by amount * 3 / 100. The accumulator is not settled inside the same AMM trade that created it.
The pair at 0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348 is a standard constant-product pool over BBOX and WBNB. Its price depends on live reserves after token balances are synchronized. Receipt log 0x149 shows the pair sync after the buy leg, and receipt log 0x14c shows a second sync after the unauthorized burn path. The second sync reduces the BBOX-side reserve while leaving the WBNB reserve unchanged at that moment.
The attack did not depend on privileged access. The adversary used a normal EOA (0x32b639c180b130ed6b785d622b5c56ff48bbdf83), a predeployed exploit executor (0xa792a5f442b07b3cb8da5867c4e6940aa85c5bb5), a helper contract created inside the exploit transaction (0xcc195b501507824b3449b3f43f2f30916fed7150), Pancake router 0x10ed43c718714eb63d5aa57b78b54704e256024e, and the permissionless DODO flash-loan pool.
3. Vulnerability Analysis & Root Cause Summary
The vulnerability is an attack-class token accounting flaw, not a flash-loan issue. The invariant that should hold is: a user transfer that does not originate from the AMM pair must not be able to reduce the pair's token inventory or move the pool price, except through legitimate AMM entrypoints.
BBOXToken._transfer breaks that invariant by coupling deferred pairAmount settlement to the next transfer sent by any non-AMM address. When the guarded branch executes, it subtracts pairAmount from _tOwned[uniswapV2Pair], credits the zero address, emits a burn Transfer, and calls IUniswapV2Pair(uniswapV2Pair).sync(). This means a regular helper-to-attacker transfer can directly mutate third-party pool balances and force reserve recalculation.
The design is exploitable because the attacker can first make a pair trade that grows pairAmount, then trigger the deferred branch from a non-AMM helper address, and finally sell against the manipulated price. The code-level breakpoint is the branch guarded by pairAmountChange && !isAddLiquidity && pairAmount > 0 && !ammPairs[from] && pairAmount < balanceOf(uniswapV2Pair) inside _transfer.
4. Detailed Root Cause Analysis
The relevant victim-side code is:
if (
pairAmountChange
&& !isAddLiquidity
&& pairAmount > 0
&& !ammPairs[from]
&& pairAmount < balanceOf(uniswapV2Pair)
) {
uint v = pairAmount;
pairAmount = 0;
_tOwned[uniswapV2Pair] = _tOwned[uniswapV2Pair].sub(v);
_tOwned[address(0)] = _tOwned[address(0)].add(v);
emit Transfer(uniswapV2Pair, address(0), v);
IUniswapV2Pair(uniswapV2Pair).sync();
}
The same function later increases pairAmount on AMM-facing trades:
if (takeFee && ammPairs[from]) {
pairAmount += amount * 3 / 100;
}
if (takeFee && ammPairs[to]) {
pairAmount += amount * 3 / 100;
require(amount <= balanceOf(from) * sellSwapLimitRate / 100, "sell limit amount");
}
The exploit sequence is deterministic:
- The executor contract takes a permissionless flash loan of
3043124720737281210661wei WBNB from the DODO pool. - It spends
1302098097375127777618wei WBNB to buy BBOX into a helper contract. This trade increasespairAmount. - The helper transfers its BBOX back to the executor. Because the sender is not an AMM pair, this ordinary transfer executes the vulnerable branch above.
- Receipt logs show the reserve mutation clearly. Log
0x149is aSyncwith BBOX reserve0x4b96d4baae9and WBNB reserve0x48cdbefd9aeae61170after the buy leg. Log0x14cis the nextSyncwith BBOX reserve0x39039c9a2awhile the WBNB reserve remains0x48cdbefd9aeae61170. - The attacker then sells BBOX back into the pool at the distorted price, repays the flash loan at receipt log
0x15a, and transfers profit at receipt log0x15c.
The collector balance diff confirms the economic effect. The pair's BBOX balance falls from 170180295995008 to 123576092647681, a net loss of 46604203347327 raw BBOX units. The attacker's profit sink receives 38225164664108721893 wei WBNB, matching the success predicate in root_cause.json.
5. Adversary Flow Analysis
The adversary cluster contains:
- EOA
0x32b639c180b130ed6b785d622b5c56ff48bbdf83, which deployed the exploit contracts and sent the exploit transaction. - Executor
0xa792a5f442b07b3cb8da5867c4e6940aa85c5bb5, created in tx0x4f7fc31a16058af040b1c888e141ff12ce617c6895d7cf7a8693b44481e1b2dc. - Helper
0xcc195b501507824b3449b3f43f2f30916fed7150, created inside the exploit transaction. - Profit sink
0xfc6d885c09f66724ba7b0e12a012d6b6bff7c84f, created in tx0x4a3179044033b0ed91c728b839730b1288e3f37d02e68e6a7870ecec34941f66.
The end-to-end on-chain flow is:
EOA -> Executor::fallback/entrypoint
Executor -> DODO::flashLoan(WBNB)
Executor -> PancakeRouter::swap WBNB -> BBOX (recipient = Helper)
Helper -> BBOXToken::transfer(Executor) // triggers pair burn + sync
Executor -> PancakeRouter::swap BBOX -> WBNB
Executor -> DODO pool (repay principal)
Executor -> Profit sink (transfer residual WBNB)
The receipt corroborates each stage. Logs 0x140 to 0x14a cover the flash-loan funding and buy leg. Logs 0x14d to 0x150 show the helper release and token-side burn activity. Logs 0x151 to 0x15d show the sell leg, flash-loan repayment, and profit transfer.
6. Impact & Losses
The measurable loss is WBNB extracted from the BBOX/WBNB pool.
- Token:
WBNB - Raw amount:
38225164664108721893 - Decimals:
18
This value was realized in the same exploit transaction after the principal was repaid. The pair also suffered a large BBOX inventory reduction: collector state diffs show the pair's BBOX balance decreased by 46604203347327 raw units. The final post-exploit reserves observed in the validated PoC were 123599336714446 BBOX and 2360523045633513214 wei WBNB, confirming severe reserve depletion.
7. References
- Exploit transaction:
0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3 - Victim token:
0x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8c - Victim pair:
0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348 - DODO flash-loan pool:
0x0fe261aee0d1c4dfddee4102e82dd425999065f4 - Verified victim source:
artifacts/collector/seed/56/0x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8c/src/Contract.sol - Transaction metadata:
artifacts/collector/seed/56/0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3/metadata.json - Balance diffs:
artifacts/collector/seed/56/0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3/balance_diff.json - Receipt logs used for validation:
artifacts/auditor/iter_0/receipt.json - Validated fork reproduction log:
artifacts/validator/forge-test.log