Reproduced Exploit
BankX Router — `swapXSDForETH` Reentrancy (BSC focus)
swapXSDForETH pulls amountInMax XSD into the pool (not the quoted amount), swaps WETH out, unwraps, and safeTransferETHs to msg.sender before burnpoolXSD(amountInMax/10).
Loss
~$43K across BSC + ETH + Optimism
Chain
BNB Chain
Category
reentrancy
Date
Feb 2025
Source
DeFiHackLabs
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Exploit reproduction, trace data, and analysis adapted from DeFiHackLabs by SunWeb3Sec — an open registry of reproduced on-chain exploits. Standalone Foundry PoC and full write-up: 2025-02-BankX_exp in the
evm-hack-registrymirror. Upstream DeFiHackLabs PoC:src/test/…/BankX_exp.sol.
Vulnerability classes: vuln/reentrancy/single-function · vuln/logic/incorrect-state-transition
Reproduction: Foundry PoC in this project folder — ONLINE BSC archive fork. Verbose run: output.txt. Router source: sources/Router.sol.
Key info#
| Loss | ~$43K across BSC + ETH + Optimism |
| Vulnerable contract | BankX Router — 0xaadae9117df8b5d584378a41a105cc4862a16e99 (BSC) |
| XSD/WETH pool | 0x8a4e0e2a778df8ce4ea5d5108fffe690cc9ae07a |
| XSD | 0x39400e67820c88a9d67f4f9c1fbf86f3d688e9f6 |
| Attacker EOA (BSC) | 0x867aa2a2667060096d0f108ddfa3367caca9fd34 |
| Attack contract (BSC) | 0xab50cfdab8484e15fee82852c08eec135ac00e4c |
| Attack txs | BSC · ETH · OP |
| Chain / block / date | BSC / 46,433,152 (fork 46,433,151) / Feb 8, 2025 |
| Bug class | ETH refund before XSD burn + full amountInMax transfer |
TL;DR#
swapXSDForETH pulls amountInMax XSD into the pool (not the quoted amount), swaps WETH out,
unwraps, and safeTransferETHs to msg.sender before burnpoolXSD(amountInMax/10).
A contract attacker reenters on receive() while pid_controller still has pricecheck=true
(setPriceCheck runs only after the outer body), performing additional swaps against a desynced pool
and burning large XSD from the pool balance.
PoC: priceCheck() → wait block_delay (2) → reentrant swapXSDForETH → 7 BNB out
(5 primary + 2 reentered) with reenterCount=2.
Vulnerable code#
function swapXSDForETH(uint amountOut, uint amountInMax, uint deadline) external ... {
(uint reserveA, uint reserveB, ) = IXSDWETHpool(XSDWETH_pool_address).getReserves();
uint amounts = BankXLibrary.quote(amountOut, reserveB, reserveA);
require(amounts <= amountInMax, 'BankXRouter: EXCESSIVE_INPUT_AMOUNT');
TransferHelper.safeTransferFrom(xsd_address, msg.sender, XSDWETH_pool_address, amountInMax);
IXSDWETHpool(XSDWETH_pool_address).swap(0, amountOut, address(this));
IWBNB(WETH).withdraw(amountOut);
TransferHelper.safeTransferETH(msg.sender, amountOut); // ← reentrancy
if (... ) {
XSD.burnpoolXSD(amountInMax/10); // ← after external call; uses full amountInMax
}
}
PoC notes#
BSC_RPC_URL=https://bsc-mainnet.public.blastapi.io forge test --match-contract BankX_exp -vv
- Requires PID
priceCheck()thenvm.roll(+block_delay). - ONLINE only (no anvil_state).
- Demonstrates reentrancy multi-swap; live attack optimized amounts across 3 chains.
References#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 2025-02-BankX_exp (evm-hack-registry mirror).
- Upstream DeFiHackLabs PoC:
BankX_exp.sol. - Attack transaction: view on explorer.
Alerts & third-party analyses
- Original alert / thread: post on X.
- DeFiHackLabs incident explorer: search "BankX Router".
- Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.