On June 18, 2026 at 16:15:57 UTC, an attacker exploited the JB token trading system on BNB Chain by abusing a privileged reserve-mutation path in the JB token contract to drain the PancakeSwap V2 JB/USDT pool. The attacker flash-borrowed 417,464.102426572951120812 WBNB, posted it to Venus, borrowed 70,000,000 USDT, bought JB once, and then ran sixteen JB sell cycles through the protocol’s route contract. Each sell cycle ended with the JB token burning JB directly from the pair address and immediately calling sync(), which let the attacker repeatedly restore a more favorable reserve ratio after already extracting USDT. The attacker repaid the Venus borrow and the flash loan and retained 49,958.056380441202939144 USDT in profit; the pair also lost 76,299,309.662855103611946106 JB from forced burns.
Root Cause
Vulnerable Contract
JB token at 0xcf92e7ef4a63d52dc15f45a24f4f815f00f299a7. This is a non-proxy token contract. Its source is only available as a recovered approximation with medium confidence, so the root-cause conclusion below is anchored in the execution trace and on-chain logs. The surrounding route contract 0x1b5732eb98911c25acf7bdfaffb9409782cae6d7 and controller 0x94741df7bf9db91b172a4e195e9bea79ce3726cd are lower-confidence recoveries and are used only as supporting context.
Vulnerable Function
Trace-reconstructed router-only function at selector 0xb1faeac6(uint256) in the JB token. The trace shows 0x1b5732eb98911c25acf7bdfaffb9409782cae6d7 calling this selector 16 times, once after each JB-to-USDT sell leg, and every call is immediately followed by the JB token calling the pair’s sync() function. In the same sell cycles, the receipt records Transfer events that move JB from the pair address 0x43932cbb49c363f68655b5ad2950ed4630cb49f8 to the zero address, proving that the function burns tokens from the pair before re-synchronizing reserves.
Vulnerable Code
// [recovered - approximation]
function func_0xb1faeac6(uint256 amount) external onlyTradeRouter returns (bool) {
_burn(slot_4_ammPair, amount); // <-- VULNERABILITY: burns tokens directly from the AMM pair balance
address pair = slot_4_ammPair;
(bool ok, bytes memory ret) = pair.call(abi.encodeWithSelector(bytes4(0xfff6cae9)));
if (!ok) {
assembly {
revert(add(ret, 0x20), mload(ret))
}
}
return true; // <-- VULNERABILITY: force-syncs reserves after an out-of-band reserve mutation
}
Why It’s Vulnerable
Expected behavior: An AMM pair’s reserves should change only through normal swaps, liquidity adds/removals, or the pair contract’s own burn flow. External token logic should not be able to destroy the pair’s balance and then force the pair to accept the new reserve state.
Actual behavior: The JB token exposes a privileged path that burns JB directly from the pair address and immediately calls sync(). The execution trace shows that the JB route contract used by the attacker can reach this path after each sell cycle. That let the attacker sell JB for USDT, then shrink the pair’s JB reserve out-of-band before the next cycle.
Because the burn happens after the USDT output has already been paid out, the pair is left with less JB but without an offsetting USDT recovery. Repeating that sequence lets the attacker keep extracting USDT while repeatedly re-anchoring the pool’s reserves around the manipulated JB balance.
Attack Execution
High-Level Flow
- The attacker flash-borrowed 417,464.102426572951120812 WBNB, deposited it into Venus, and borrowed 70,000,000 USDT.
- The attacker sent the 70,000,000 USDT into the JB route contract
0x1b5732eb98911c25acf7bdfaffb9409782cae6d7, which bought 119,907,137.388193202146690518 JB from the PancakeSwap V2 JB/USDT pair. - The attacker then executed sixteen JB-to-USDT sell rounds through the same route contract.
- After each sell, the route contract invoked JB token selector
0xb1faeac6(uint256), which burned JB directly from the pair and then calledsync()on the pair. - After the final loop, the attacker repaid the Venus borrow, redeemed the WBNB collateral, repaid the flash loan, and sent 49,958.056380441202939144 USDT to the attacker EOA.
Detailed Call Trace
The attacker EOA called 0xcc21c75f9e13054667663f9ed37f41e65b52dee7, which flash-borrowed WBNB from 0x8f73b65b4caaf64fba2af91cc5d4a2a1318e5d8c. Inside the flash-loan callback, that executor entered Venus, minted vWBNB with the borrowed WBNB, and borrowed 70,000,000 USDT from the Venus USDT market. The executor then called the JB route contract once with selector 0xd680aabd to buy JB, and sixteen times with selector 0x53a9fcbc to unwind JB back into USDT.
Inside each sell round, the route contract transferred JB from the executor, made two calls into 0x94741df7bf9db91b172a4e195e9bea79ce3726cd with selectors 0x727ebe6d and 0x0e2aa265, and those calls each pulled JB from the route contract into 0x94741df7bf9db91b172a4e195e9bea79ce3726cd via transferFrom. The route contract also transferred JB to 0x28301fe4f3cc1f4f6da2ec2c6a7bb9c291cf6eb0, then approved the Pancake router and sold the remaining JB into the JB/USDT pair. After the router delivered USDT back to the route contract and the route contract forwarded that USDT to the executor, the route contract called JB token selector 0xb1faeac6. That token call was followed immediately by JBToken -> PancakePair.sync(), and the receipt shows a matching Transfer(pair, 0x0000000000000000000000000000000000000000, amount) burn in each cycle. Across the transaction, the trace shows 17 calls each to 0x727ebe6d and 0x0e2aa265, 16 sell calls through 0x53a9fcbc, 16 calls to JB token selector 0xb1faeac6, and 16 post-burn sync() calls.
The trace does not show 0x94741df7bf9db91b172a4e195e9bea79ce3726cd minting JB back to the attacker path during this transaction. Instead, it only receives JB from the route contract through transferFrom, so the on-chain evidence supports reserve manipulation through forced pair burns rather than an in-transaction JB mint.
Financial Impact
The attacker retained 49,958.056380441202939144 USDT, which is the net loss to the PancakeSwap V2 JB/USDT pool and approximately $49.96K. The pair also lost 76,299,309.662855103611946106 JB through forced burns to the zero address. Separately, 12,848,049.771144900470972061 JB was siphoned from the route contract into 0x94741df7bf9db91b172a4e195e9bea79ce3726cd, and 11,684,950.538479428738355637 JB was diverted to 0x28301fe4f3cc1f4f6da2ec2c6a7bb9c291cf6eb0 during the route’s internal accounting. The WBNB flash loan and the Venus borrow were fully repaid inside the same transaction.
Evidence
- Transaction:
0x54e120b8d62a9d7cef94bf51f1f5b8aa13565d76d8797a79afeeb25ed0e1dc25on BNB Chain, block104980467, statussuccess. - Attacker:
0xd99e1abfc5dd5034d7ff63828d16c5e945d1b856. - Vulnerable contract:
0xcf92e7ef4a63d52dc15f45a24f4f815f00f299a7. - Impacted pool/token/protocol component: PancakeSwap V2 JB/USDT pair
0x43932cbb49c363f68655b5ad2950ed4630cb49f8. - Key on-chain fact: the attacker borrowed
70,000,000 USDTafter a417,464.102426572951120812 WBNBflash loan and then used oneUSDT -> JBbuy plus sixteenJB -> USDTsell rounds through0x1b5732eb98911c25acf7bdfaffb9409782cae6d7. - Key on-chain fact: the transaction contains sixteen JB burns from the pair to the zero address totaling
76,299,309.662855103611946106 JB, and each burn is followed byJBToken -> PancakePair.sync(). - Key on-chain fact:
0x94741df7bf9db91b172a4e195e9bea79ce3726cdreceives JB via thirty-fourtransferFromcalls totaling12,848,049.771144900470972061 JB, but the trace does not show it sending JB back to the attacker path during this transaction. - Key on-chain fact: the executor contract transferred
49,958.056380441202939144 USDTto the attacker EOA at the end of the transaction.
Remediation
Remove any token or router function that can burn tokens directly from an AMM pair address or call sync() after mutating pair-held balances. Reserve updates must stay inside the pair’s native swap and liquidity flows. If special routing logic is required, it should never be able to mutate the pair’s inventory out-of-band.
The JB route contract should also stop exposing privileged token-maintenance paths as part of user trade flows. Any route that reaches reserve-affecting token logic must be admin-only, tightly scoped, and separated from user trading entry points. Finally, add invariant tests that assert: pair-held token balances cannot be reduced by external token logic, sync() cannot be user-triggered after arbitrary token burns, and a full sell route cannot improve the next trade’s price by mutating reserves outside normal AMM math.