We do not have a reliable USD price for the recorded assets yet.
0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e3330x7ac55ac530f2c29659573bde0700c6758d69e677BSCOn BSC block 7781160, transaction 0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333 exploited BurgerSwap Demax by combining permissionless pair creation with router-level token callback reentrancy. The attacker first bought DGAS with WBNB, then created a fresh fake-token/DGAS pair, then used that fake token in a route whose transferFrom reentered DemaxPlatform.swapExactTokensForTokens before the outer route completed. The nested DGAS sale manipulated the live DGAS/WBNB pair reserves, the resumed outer route drained almost all WBNB from that victim pair, and a final WBNB-to-DGAS buyback left the attacker with 110562238468881555058642 DGAS.
The root cause is a protocol bug, not pure MEV. Demax accepted arbitrary attacker-created tokens because DemaxConfig was configured with LIST_TOKEN_SWITCH = 0, and DemaxPlatform trusted arbitrary token transferFrom calls to be side-effect free while executing a swap path. The protocol therefore allowed an attacker-controlled token to reenter the router between path pricing and route completion.
BurgerSwap Demax is a router-and-pair AMM design. The relevant victim liquidity pool is the DGAS/WBNB pair at 0x7ac55ac530f2c29659573bde0700c6758d69e677, while the vulnerable router is DemaxPlatform at 0xbf6527834dbb89cdc97a79fcd62e6c08b19f8ec0, and the listing/configuration contract is at .
DemaxConfig0xe7f6824706aeee33542088eb2fdd2d69e37455b6The first prerequisite is that arbitrary tokens were routable. The verified DemaxConfig source shows that the listing switch was initialized to zero and that checkToken() therefore accepts any token:
_initConfig(ConfigNames.LIST_TOKEN_SWITCH, 0, 1, 1, 0); // 0:off, 1:on
function checkToken(address _token) public view returns(bool) {
if (getConfigValue(ConfigNames.LIST_TOKEN_SWITCH) == 0) {
return true;
}
...
}
The second prerequisite is that the router performs an external token transfer before the route is finished. In DemaxPlatform, swapExactTokensForTokens computes amounts, transfers the first input token into the first pair with _innerTransferFrom, and only then executes _swap:
amounts = _getAmountsOut(amountIn, path, percent);
address pair = DemaxSwapLibrary.pairFor(FACTORY, path[0], path[1]);
_innerTransferFrom(path[0], msg.sender, pair, ...);
_swap(amounts, path, to);
The transfer itself is an arbitrary ERC20 call:
function _innerTransferFrom(address token, address from, address to, uint256 amount) internal {
TransferHelper.safeTransferFrom(token, from, to, amount);
_transferNotify(from, to, token, amount);
}
The pair contract has a local lock() modifier on mint, burn, and swap, but that lock only protects an individual pair call. It does not protect the router from being reentered by a hostile token during the router’s own transferFrom:
uint private unlocked = 1;
modifier lock() {
require(unlocked == 1, 'Locked');
unlocked = 0;
_;
unlocked = 1;
}
The vulnerability is router-level reentrancy induced by an attacker-controlled token callback. The attacker was able to permissionlessly list a fake token, create a fake-token/DGAS pair, and then route a fake-token swap through DemaxPlatform. Because the router priced the route first and then called the fake token’s transferFrom, the attacker token could execute arbitrary logic before the outer route consumed the already-priced victim reserves.
The explicit invariant is: once a router computes route amounts from current reserves, the reserve state relevant to that route must remain unchanged until route execution finishes, except for the intended updates of that same route. Demax violated that invariant by allowing arbitrary token code to run after _getAmountsOut(...) but before _swap(...) finished.
The concrete breakpoint is inside DemaxPlatform.swapExactTokensForTokens at the first _innerTransferFrom(path[0], msg.sender, pair, ...) on the attacker-controlled fake token. During that call, the token reentered DemaxPlatform and executed a nested DGAS-to-WBNB swap against the real victim pair. After the nested call changed the victim reserves, the outer route resumed and still consumed the old route assumptions, producing the reserve collapse that made the final DGAS buyback profitable.
The attacker token decompile supports the callback behavior. Its transfer logic performs an external call to a stored address after balance updates, which is consistent with the trace-observed callback:
require(address(unresolved_b2bdfa7b).code.length);
(bool success, bytes memory ret0) =
address(unresolved_b2bdfa7b).Unresolved_e2007c01(var_e);
The exploit started from public BSC state immediately before the seed transaction. The DGAS/WBNB pair already held significant real liquidity, and the protocol configuration allowed arbitrary token listing and pair creation. The attacker needed no privileged key material and used only public protocol entrypoints.
First, the attacker acquired DGAS by swapping 6047132230250298663393 WBNB through Demax. The fork and receipt evidence show that this produced 92677046404962134264971 DGAS, which funded the later fake-token pair and the nested callback sale.
Second, the attacker created a fake-token/DGAS pair inside the same transaction. The trace shows DemaxFactory::createPair(0xA61275f7..., DGAS) and the surrounding DemaxConfig::checkToken(...) calls both returning successfully. The resulting pair 0x7704c4f30c1203cab03d87836932f01a9497d6e9 was funded with 100 fake-token units and 45452972851691664175156 DGAS.
Third, the attacker triggered the actual breakpoint by routing a fake-token swap through [fakeToken, DGAS, WBNB]. The seed trace captures the exact nested call sequence:
0xA61275f7...::transferFrom(attacker, 0x7704c4f3..., 9970000000)
0xAE0F5384...::transferCallback(0x7704c4f3...)
DemaxPlatform::swapExactTokensForTokens(
45452972851691664175157,
0,
[DGAS, WBNB],
attacker,
1622141686
)
Immediately before the nested sale, the victim DGAS/WBNB pair reserves were 46618433694042732487340 DGAS and 9085892144948941207811 WBNB. The nested DGAS sale transferred 45316613933136589182631 DGAS into the victim pair and caused the victim pair to pay out 4478616993167725377880 WBNB, updating reserves to 91935047627179321669971 DGAS and 4607275151781215829931 WBNB.
After that nested reserve change, the outer fake-token route resumed. The fake-token/DGAS pair sent 45452972395794247968593 DGAS into the victim pair, and the victim pair then paid out another 4478616970389214164363 WBNB on the outer route. The trace and fork both show the victim pair’s WBNB balance falling to 128658181392001665568, which is the manipulated, nearly drained state that made the final buyback possible:
emit Sync(: 137388020022973569638564, : 128658181392001665568)
emit Swap(... amount4: 4478616970389214164363, param5: attacker)
Finally, the attacker spent 493279295339485109551 WBNB to buy back 108791137767302749143983 DGAS from the now-mispriced victim pair. The reproduced fork run ends with the attacker holding 110562238468881555058642 DGAS, while the victim pair’s DGAS inventory drops by more than 110000e18. That matches the incident-scale profit predicate and demonstrates that callback-token router reentrancy, rather than a special fake-token allowance quirk, is the determinative cause.
The adversary cluster in the incident consists of the EOA 0x6c9f2b95ca3432e5ec5bcd9c19de0636a23a4994, the orchestrator 0xae0f538409063e66ff0e382113cb1a051fc069cd, the attacker-created callback token 0xa61275f7fbd1959d2a1c9a298e602929f412d2e1, the helper 0xd0dd735851c1ca61d0324291ccd3959d2153a88d, and the wrapper 0x1a21cc36242dc733cf6bde2ff19437b0da7a2b4e. The victim asset reservoir is the DGAS/WBNB pair 0x7ac55ac530f2c29659573bde0700c6758d69e677.
The transaction-level flow is:
addLiquidity(fakeToken, DGAS, ...) to create and fund a fresh fake-token/DGAS pair.swapExactTokensForTokens(fakeToken -> DGAS -> WBNB).transferFrom, reenter DemaxPlatform and execute swapExactTokensForTokens(DGAS -> WBNB).This is ACT because every step is permissionless, uses canonical on-chain state, and can be performed by any unprivileged actor with capital and routing logic. The presence of attacker-controlled helper contracts does not make the opportunity non-ACT; those contracts are ordinary user-deployed exploit tooling.
The directly measured victim loss is incident-scale DGAS depletion from the real DGAS/WBNB pair. The recorded loss amount is 110995595927960880175547 DGAS smallest units, with token decimals 18. The attacker’s realized DGAS balance at the end of the exploit transaction is 110562238468881555058642.
Operationally, the exploit forced the victim pair from a healthy reserve state to a near-empty WBNB state during the manipulation stage, leaving only 128658181392001665568 WBNB before the final buyback. That reserve collapse made the final WBNB-to-DGAS swap extraordinarily favorable to the attacker and converted the routing bug into a realized asset loss.
0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333./workspace/session/artifacts/collector/iter_1/tx/56/0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333/receipt_raw_rpc.json./workspace/session/artifacts/collector/iter_1/tx/56/0xac8a739c1f668b13d065d56a03c37a686e0aa1c9339e79fcbc5a2d0a6311e333/trace.cast.log./workspace/session/artifacts/collector/iter_1/contract/56/0xa61275f7fbd1959d2a1c9a298e602929f412d2e1/heimdall_decompile/decompiled.sol./workspace/session/artifacts/collector/iter_1/contract/56/0x7704c4f30c1203cab03d87836932f01a9497d6e9/source_from_etherscan.sol.https://bscscan.com/address/0xe7f6824706aeee33542088eb2fdd2d69e37455b6#code.https://bscscan.com/address/0xbf6527834dbb89cdc97a79fcd62e6c08b19f8ec0#code./workspace/session/artifacts/auditor/forge-test.log.