Calculated from recorded token losses using historical USD prices at the incident time.
0xe8b0131fa14d0a96327f6b5690159ffa7650d66376db87366ba78d91f17cd6770xde698b5bbb4a12ddf2261bbdf8e034af34399999BSC0xc0f54b8755daf1fd78933335efcd761e3d5b4a6fBSCOn BNB Chain block 37221236, transaction 0xe8b0131fa14d0a96327f6b5690159ffa7650d66376db87366ba78d91f17cd677 drained the ARK/WBNB Pancake pair at 0xc0f54b8755daf1fd78933335efcd761e3d5b4a6f. The attacker-controlled helper first called AbsToken.autoBurnLiquidityPairTokens() in a loop, then bypassed ARK sell fees, then swapped a small ARK amount for 348.147268515623217950 WBNB.
The root cause is a public reserve-destructive function in the ARK token at 0xde698b5bbb4a12ddf2261bbdf8e034af34399999. autoBurnLiquidityPairTokens() directly transfers ARK out of _mainPair to 0x000000000000000000000000000000000000dEaD and immediately calls sync() without access control and without enforcing the cooldown that exists only in _transfer()'s sell path.
ARK is a custom token that creates and tracks its Pancake pair in _mainPair. Because the token contract owns the _balances mapping for ARK, it can arbitrarily rewrite the ARK balance held by the pair.
PancakePair pricing depends on stored reserves. Calling sync() updates the pair's reserves to match current token balances, so if a token contract burns assets directly out of the pair and then calls , the AMM price moves even though no normal AMM burn occurred.
sync()ARK also contains sell-fee logic that checks _isAddLiquidity(). That helper compares the paired-asset balance against the stored reserve, so a tiny WBNB transfer to the pair makes the subsequent ARK transfer look like liquidity addition and disables the sell fee.
The vulnerability is an access-control and invariant-breaking attack in the token contract, not an AMM flaw in PancakePair. ARK exposes autoBurnLiquidityPairTokens() as a public function even though it directly reduces the pair's ARK balance and updates reserves. The intended LP burn throttle exists only inside _transfer() when a user sells into the pair, but autoBurnLiquidityPairTokens() itself does not check lpBurnFrequency before executing. As a result, any caller can invoke the function repeatedly in one transaction and push the pair's ARK reserve toward zero while leaving WBNB reserves intact. Once reserves are skewed, the attacker only needs a small ARK amount to extract almost all WBNB from the pool. The exploit becomes even more efficient because ARK's _isAddLiquidity() heuristic can be manipulated with a 100 wei WBNB transfer, which disables the normal sell fee on the final ARK transfer into the pair.
The verified ARK source shows the vulnerable path plainly:
function autoBurnLiquidityPairTokens() public {
lastLpBurnTime = block.timestamp;
uint256 liquidityPairBalance = balanceOf(_mainPair);
uint256 amountToBurn = (liquidityPairBalance * percentForLPBurn) / 10000;
if (amountToBurn > 0) {
_basicTransfer(_mainPair, address(0xdead), amountToBurn);
}
ISwapPair(_mainPair).sync();
emit AutoNukeLP();
}
This function is publicly callable and does not require onlyOwner, whitelist membership, or a cooldown check. The invariant that the pair's ARK reserve should change only through legitimate AMM flows is therefore broken at the token level.
The sell path in _transfer() shows the intended but incomplete throttle:
if (_swapPairList[to] && startTradeBlock != 0) {
if (!inSwap && !isAdd) {
if (
lpBurnEnabled &&
block.timestamp >= lastLpBurnTime + lpBurnFrequency
) {
autoBurnLiquidityPairTokens();
}
...
}
}
The cooldown is enforced only when _transfer() decides to call autoBurnLiquidityPairTokens() internally. A direct external call bypasses that condition entirely.
The seed trace proves the exploit used that gap. The transaction repeatedly invokes AbsToken::autoBurnLiquidityPairTokens(), and every call emits a transfer from the pair to the dead address followed by PancakePair::sync():
AbsToken::autoBurnLiquidityPairTokens()
emit Transfer(src: PancakePair, dst: 0x...dEaD, wad: 3694730594480279771)
PancakePair::sync()
emit AutoNukeLP()
...
AbsToken::autoBurnLiquidityPairTokens()
emit Transfer(src: PancakePair, dst: 0x...dEaD, wad: 3683646402696838931)
PancakePair::sync()
The same trace later shows the fee-bypass setup and the draining swap:
WBNB::transfer(PancakePair, 100)
AbsToken::transferFrom(0x76b5587f..., PancakePair, 3154246100519930675)
PancakePair::swap(348147268515623217950, 0, 0xdd309ea4..., 0x)
Immediately before the final swap, getReserves() returns roughly 348.147454452287791423 WBNB against only 1680223642981 wei of ARK reserve, showing the reserve collapse. The subsequent swap transfers 348.147268515623217950 WBNB to the attacker and leaves only 185936664573573 wei WBNB in the pair.
Balance diffs independently confirm the state transition. The pair's ARK balance falls from 1231576864826759923698 to 3153932356133521662, the dead address gains 1231576863146536280717 ARK, and the attacker EOA receives 348147268515623217950 WBNB while paying 241758819000000000 wei in gas.
The adversary sequence is a single attacker-crafted transaction sent by EOA 0xdd309ea4e99b772c1e6a798a6b159211bb95b22c to helper contract 0x94598ec1eb8f85d57fba787df6b49dbbabb87f37.
Step 1: the helper repeatedly calls autoBurnLiquidityPairTokens(). Each call burns percentForLPBurn / 10000 of the current pair-held ARK balance and syncs the pair, so the ARK reserve decays geometrically inside one transaction.
Step 2: the helper sends 100 wei WBNB to the pair. This makes _isAddLiquidity() observe balanceOf(WBNB, pair) > reserveWBNB, so the upcoming ARK transfer is treated as add-liquidity and not as a fee-bearing sell.
Step 3: the helper pulls ARK from 0x76b5587f13181d131269042642ec224107dd6ab0 through transferFrom, consuming pre-existing allowance and moving 3153930675909878681 wei ARK into the pair. The trace storage updates show the allowance slot decrementing during this transfer.
Step 4: the helper computes the swap output against the distorted reserves and calls PancakePair.swap, transferring 348.147268515623217950 WBNB to the attacker EOA. The pair is left with negligible WBNB and the exploit predicate is realized.
The exploit is ACT because every critical action uses public functionality: a public token function, standard ERC-20 transfers and allowances, and a public Pancake swap. No privileged key or hidden state is required.
The measurable loss is the WBNB drained from the ARK/WBNB Pancake pair:
WBNB: 348147268515623217950 raw units (348.147268515623217950 WBNB), decimal=18In addition, 1231576863146536280717 raw ARK units were burned to 0x000000000000000000000000000000000000dEaD during reserve collapse. That burn is part of the exploit path, but the directly monetized loss in the incident is the WBNB extracted from the pair.
0xe8b0131fa14d0a96327f6b5690159ffa7650d66376db87366ba78d91f17cd6770xde698b5bbb4a12ddf2261bbdf8e034af34399999 (AbsToken)0xc0f54b8755daf1fd78933335efcd761e3d5b4a6f