Calculated from recorded token losses using historical USD prices at the incident time.
0xae8ca9dc8258ae32899fe641985739c3fa53ab1f603973ac74b424e165c66ccf0x32b166e082993af6598a89397e82e123ca44e74eBSC0xf375709dbde84d800642168c2e8ba751368e8d32BSCOn BNB Chain transaction 0xae8ca9dc8258ae32899fe641985739c3fa53ab1f603973ac74b424e165c66ccf at block 22337426, an unprivileged attacker exploited a flaw in the HEALTH token at 0x32b166e082993af6598a89397e82e123ca44e74e to manipulate the Pancake HEALTH/WBNB pair at 0xf375709dbde84d800642168c2e8ba751368e8d32. The attacker bought HEALTH, executed 1000 zero-value HEALTH transfers, then sold back into the now-distorted pool price and exited with 16.641927146106351887 WBNB before gas and 16.289384036106351887 WBNB net after gas.
The root cause is a token-side accounting flaw inside HEALTH._transfer. The function permits value == 0, but still burns burnFee / 1000 of the pair's HEALTH balance and calls sync() whenever the sender is not the pair and the time gate is open. That lets any public caller repeatedly shrink the pair's HEALTH reserve without paying HEALTH into the pair, which directly inflates the apparent HEALTH price against WBNB.
HEALTH is a BEP-20 style token that created a Pancake pair against WBNB through router 0x10ed43c718714eb63d5aa57b78b54704e256024e. In a Pancake-style AMM, the pair price is determined by reserves, so any external code path that changes pair balances and then calls sync() changes the trading price immediately.
The critical token configuration is public and readable on-chain:
uniswapV2Pair points to the HEALTH/WBNB pair.pairStartTime and jgTime gate a periodic burn branch.burnFee determines how much pair-held HEALTH is destroyed each time that branch runs.The exploit does not require privileged roles, private keys, or attacker-specific artifacts. Once the burn window is active, any non-pair caller can hit the same public transfer path.
The vulnerability is an ATTACK-class token design flaw, not an oracle issue or router bug. HEALTH embeds market-moving logic inside the unrestricted _transfer path and does so independently of the user-supplied transfer amount. The function only checks value <= _balances[from], so value == 0 is accepted. After the burn window opens, any call where from != uniswapV2Pair computes a burn amount from the pair's current HEALTH balance, debits the pair, credits the dead address, and calls IPancakePair(uniswapV2Pair).sync(). Because this happens before the transfer's own value-dependent accounting matters economically, a zero-value transfer becomes a state-changing reserve manipulation primitive. Repeating that primitive steadily removes HEALTH from the pair while leaving WBNB in place, which raises the price at which the attacker can later sell HEALTH back to the pool.
The violated invariant is straightforward: a public token transfer, especially a zero-value transfer, must not arbitrarily reduce AMM reserves or reprice the pool unless the pair is directly participating and the reserve change is implied by the transferred amount. HEALTH breaks that invariant inside _transfer.
The verified HEALTH source shows the exact breakpoint:
function _transfer(address from, address to, uint256 value) private {
require(value <= _balances[from]);
require(to != address(0));
...
if (block.timestamp >= pairStartTime.add(jgTime) && pairStartTime != 0) {
if (from != uniswapV2Pair) {
uint256 burnValue = _balances[uniswapV2Pair].mul(burnFee).div(1000);
_balances[uniswapV2Pair] = _balances[uniswapV2Pair].sub(burnValue);
_balances[_burnAddress] = _balances[_burnAddress].add(burnValue);
...
emit Transfer(uniswapV2Pair,_burnAddress, burnValue);
IPancakePair(uniswapV2Pair).sync();
}
}
...
}
This code comes from the verified HEALTH token source collected for 0x32b166e082993af6598a89397e82e123ca44e74e. The key observations are:
value == 0 is legal because the only amount check is require(value <= _balances[from]).from != uniswapV2Pair, not on value > 0._balances[uniswapV2Pair], not from the caller.sync() is called immediately after the burn, so the AMM adopts the manipulated reserve state on each trigger.The transaction trace for 0xae8ca9dc... confirms that the attacker exploited exactly this path. It contains 1000 repeated calls of the form:
token::transfer(0xDE78112FF006f166E4ccfe1dfE4181C9619D3b5D, 0)
PancakePair::sync()
The collected trace contains that zero-value transfer pattern exactly 1000 times. The balance-diff artifact independently confirms the reserve effect:
{
"token": "0x32b166e082993af6598a89397e82e123ca44e74e",
"holder": "0xf375709dbde84d800642168c2e8ba751368e8d32",
"delta": "-21941109637301307403350681"
}
and the dead address gains burned HEALTH:
{
"token": "0x32b166e082993af6598a89397e82e123ca44e74e",
"holder": "0x000000000000000000000000000000000000dead",
"delta": "20073813435839968368224860"
}
That evidence matches the stated exploit conditions in root_cause.json: the burn window had to be active, burnFee had to be positive, the configured pair had to hold liquidity, and the attacker needed enough WBNB to acquire HEALTH before triggering the loop.
The adversary cluster identified in the evidence is:
0xde78112ff006f166e4ccfe1dfe4181c9619d3b5d, which submitted the transaction and received the final WBNB profit.0x80e5fc0d72e4814cb52c16a18c2f2b87ef1ea2d4, which performed the exploit steps on-chain.The end-to-end sequence is deterministic:
0x0fe261aee0d1c4dfddee4102e82dd425999065f4 transfers 40 WBNB to the helper.40 WBNB into HEALTH.transfer(..., 0) 1000 times from a non-pair address, repeatedly burning pair-held HEALTH and syncing reserves.40 WBNB to the funding source and forwards the residual WBNB to the submitting EOA.The trace shows the profit leg directly:
WBNB::transfer(0x80e5FC0d72e4814cb52C16A18c2F2B87eF1Ea2d4, 56641927146106351887)
WBNB::transfer(0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4, 40000000000000000000)
WBNB::transfer(0xDE78112FF006f166E4ccfe1dfE4181C9619D3b5D, 16641927146106351887)
Those values line up with the narrative in root_cause.json: the manipulated exit yields 56.641927146106351887 WBNB, 40 WBNB is repaid, and 16.641927146106351887 WBNB remains for the attacker. The trace also shows a smaller WBNB movement to the configured dev address, which explains the token's incidental auto-swap side effect during the sell.
The HEALTH/WBNB pair is the directly harmed pool. The measurable reserve-value loss captured in the artifacts is:
16.675217568865166316 WBNB lost from the pair.21,941,109.637301307403350681 HEALTH removed from the pair over the transaction.19,768,106.913152402811469624 HEALTH burned directly from the pair during the repeated zero-transfer loop.The attacker received 16.641927146106351887 WBNB, while the submitting EOA paid 0.35254311 BNB in gas. The resulting net profit is therefore 16.289384036106351887 WBNB. The configured dev address also received 0.033290422758814429 BNB from HEALTH's sell-side fee swap, which is incidental to the exploit and not the core loss mechanism.
0xae8ca9dc8258ae32899fe641985739c3fa53ab1f603973ac74b424e165c66ccf on BNB Chain.0x32b166e082993af6598a89397e82e123ca44e74e.22337426.