STOToken Sell-Hook Reserve Manipulation Drains the STO/WBNB Pancake Pair
Exploit Transactions
0xf7f741bf17299050a6e068f0af6eabc22f6389301d18b3c610537d7cc4422e03Victim Addresses
0xc6941c6bffdc844073e2c7c22816216c3890cd65BSC0xa4e56ac9aa21184a0f68e2963b51146a4f556495BSCLoss Breakdown
Similar Incidents
STO Pending-Sell Burn Reserve Manipulation
52%SOF Sell-Hook Reserve Manipulation Drains PancakeSwap V2 USDT Liquidity
47%XDK Sell-Hook Reserve Theft on PancakePair
42%MetaverseToken fee misconfiguration drains USDT from Pancake pair
38%0x3Af7 burn-rate manipulation drains WBNB from BSC pool
36%CS Pair Balance Burn Drain
36%Root Cause Analysis
STOToken Sell-Hook Reserve Manipulation Drains the STO/WBNB Pancake Pair
1. Incident Overview TL;DR
At BSC block 82257843, transaction 0xf7f741bf17299050a6e068f0af6eabc22f6389301d18b3c610537d7cc4422e03 performs a two-leg route (WBNB -> STO, then STO -> WBNB) through Pancake Router and extracts value from pair 0xa4e56ac9aa21184a0f68e2963b51146a4f556495.
The root cause is in STOToken (0xc6941c6bffdc844073e2c7c22816216c3890cd65): during sell transfers to the pair, token code burns pair-held STO and calls sync() before finishing the seller transfer to the pair. That mid-swap reserve rewrite violates AMM reserve-accounting assumptions and enables outsized WBNB payout.
2. Key Background
- Pair model: Pancake V2 pricing/invariant checks assume reserves are updated from canonical token-in/token-out flow.
- Victim token behavior:
STOToken::_updateincludes custom buy/sell logic and a pre-sell burn hook. - ACT framing: no privileged role is required; any unprivileged actor can route swaps against public liquidity if sell path is open.
The exploitable condition is not private-key or admin dependent. It is a deterministic side effect of token transfer logic interacting with AMM reserve snapshots.
3. Vulnerability Analysis & Root Cause Summary
The issue is an ATTACK-class logic flaw at the token/AMM integration boundary. In the sell branch, STOToken computes a sellAmount, then executes _burn(lpPair, burnFromPair) and IUniswapV2Pair(lpPair).sync() before super._update(from, to, sellAmount). This allows the pair to record an artificially low STO reserve during swap pricing. Router/pair logic then executes against the manipulated reserve state, paying an excessive WBNB amount. Immediately after, token transfer settlement restores STO balance while quote-side liquidity has already been extracted.
Invariant violated:
- During swap-critical execution, pair reserves should not be externally rewritten by token hooks beyond canonical transfer accounting.
Code-level breakpoint:
STOToken::_updatesell path executes_burn(lpPair, burnFromPair); sync();before seller transfer completion.
4. Detailed Root Cause Analysis
- The incident transaction is replayable from public pre-state (
metadata,trace, and verified token source are available). - The adversary performs a buy leg to load WBNB into the pair and prepare for the sell-triggered hook.
- On the sell leg (
to == lpPair), token logic enters the vulnerable sequence:
uint256 pairBal = super.balanceOf(lpPair);
uint256 burnFromPair = sellAmount > pairBal ? pairBal : sellAmount;
if (burnFromPair > 0) {
_burn(lpPair, burnFromPair);
IUniswapV2Pair(lpPair).sync();
emit PreSellBurn(burnFromPair);
}
super._update(from, to, sellAmount);
- Trace evidence shows reserve manipulation before payout:
emit Transfer(from: PancakePair, to: 0x000..., amount: 1355905591116876893542)
PancakePair::sync()
emit Sync(reserve0: 682394686810760079681353, reserve1: 9)
emit PreSellBurn(amountBurned: 1355905591116876893542)
- The pair then pays out WBNB using the manipulated reserve context:
PancakePair::swap(682394686810760079676812, 0, attacker, 0x)
WBNB::transfer(attacker, 682394686810760079676812)
emit Sync(reserve0: 4541, reserve1: 1355905591116876893551)
- Final state confirms severe quote-side depletion (
reserve0 = 4541) and successful extraction.
Exploit conditions:
isSellOpen == true.- Pair has non-zero STO so
burnFromPair > 0can execute. - Public router access (no privileged permissions).
5. Adversary Flow Analysis
Adversary cluster:
0x87a8ff8ad993c10af4ad85b62ddb50b4968abc93(EOA sender and payout beneficiary in incident tx).
Victim components:
STOToken:0xc6941c6bffdc844073e2c7c22816216c3890cd65PancakePair(STO/WBNB):0xa4e56ac9aa21184a0f68e2963b51146a4f556495PancakeRouterV2:0x10ed43c718714eb63d5aa57b78b54704e256024e
End-to-end sequence inside tx 0xf7f741...22e03:
- Buy leg:
swapExactTokensForTokensSupportingFeeOnTransferTokenson path[WBNB, STO], outputting7820156662440489446414626STO. - Sell trigger: STO transfer to pair executes burn+sync hook and emits
PreSellBurn. - Extraction: pair swap pays
682394686810760079676812WBNB; pair WBNB reserve collapses to4541.
6. Impact & Losses
Measured impact in incident tx:
- Gross round-trip extraction in WBNB terms:
118001474297520951667wei. - Native gas cost observed:
574744117500000wei BNB. - Pool health impact: WBNB reserve of STO/WBNB pair drops to near-zero (
4541), representing immediate liquidity depletion.
7. References
- Incident tx metadata:
/workspace/session/artifacts/collector/seed/56/0xf7f741bf17299050a6e068f0af6eabc22f6389301d18b3c610537d7cc4422e03/metadata.json - Incident full trace:
/workspace/session/artifacts/collector/seed/56/0xf7f741bf17299050a6e068f0af6eabc22f6389301d18b3c610537d7cc4422e03/trace.cast.log - Balance deltas:
/workspace/session/artifacts/collector/seed/56/0xf7f741bf17299050a6e068f0af6eabc22f6389301d18b3c610537d7cc4422e03/balance_diff.json - STOToken source (breakpoint):
/workspace/session/artifacts/collector/seed/56/0xc6941c6bffdc844073e2c7c22816216c3890cd65/src/STOTOKEN.sol