Calculated from recorded token losses using historical USD prices at the incident time.
0xb7e1d1372f2880373d7c5a931cdbaa73c38663c6BSC0x0df9d225ccfaa21ceb0b2ab6855b13dffa78d253BSC0x7570fdad10010a06712cae03d2fc2b3a53640aa4BSC0x28221c875bd823b73de945ac590411bc87aa89b2BSCProtocol: BNB Chain marketplace proxy 0xb7e1d1372f2880373d7c5a931cdbaa73c38663c6 with reward token proxy 0x7570fdad10010a06712cae03d2fc2b3a53640aa4
Category: Protocol logic / access-control bug (root_cause_category = protocol_bug)
ACT opportunity: Yes (is_act = true)
On BNB Chain (chainid 56), a fresh externally owned account (EOA) 0x9f2ecec0145242c094b17807f299ce552a625ac5 deployed a constructor-only helper contract and, within the same transaction, invoked an unprotected function with selector 0x9b3e9b92 on marketplace proxy 0xb7e1.... This call drained the proxy’s entire on-chain USDT balance of 8,484.92 units and minted 33,939.68 units of the associated reward token 0x7570... to the helper contract.
The helper immediately swapped the stolen USDT through a public PancakeSwap USDT/WBNB pair into approximately 13.041098705767818172 WBNB, which was unwrapped to BNB and delivered back to the EOA. After accounting for gas and AMM slippage, the attacker realized a net profit of about 13.039081184767818172 BNB. In subsequent blocks, the attacker deposited most of this BNB into a Tornado-like mixer contract to obfuscate the proceeds.
Root cause (brief): The implementation behind proxy 0xb7e1... exposes a function at selector 0x9b3e9b92 that any address can call. Instead of operating on per-user order balances, this function uses the proxy’s global and reward-token minting to move all pooled USDT and newly minted reward tokens to an arbitrary recipient, with no access control and no requirement that the caller has any prior deposit or order in the system.
0x15395547c4ccf60829b54c9ac323380ad5e55e762a84b360d987fff986c61c440xfbb8190db800fbca7e83e69adb2fef01093159b45f14d70c0edac604767366b90x91faff27ec91d08320a8d35950f9fc6155d841d5ad7f8cfd20534c6a6a01832a0xccc2a77e4ec7809d9550ea1b7d87c3b5a7847fa72523823e87a6132cd0ebbf52USDT.balanceOfProxy architecture and roles
0xb7e1... and reward-token proxy 0x7570... were both deployed on BNB Chain by creator EOA 0xbeb28a030fec8009157d112550e7e2f0b7683c40 as TransparentUpgradeableProxy instances.0xb7e1... points to implementation 0x0df9d225ccfaa21ceb0b2ab6855b13dffa78d253, while 0x7570... points to implementation 0x28221c875bd823b73de945ac590411bc87aa89b2.0x55d398326f99059ff775485246999027b3197955 as the settlement token, 0x7570... as a trading or reward token, and sets owner/admin roles via a ProxyAdmin.Marketplace implementation (0x0df9d2...)
cancelOrder(uint256) and editOrder(uint256,uint256):
msg.sender to equal the stored order owner.transfer / transferFrom for amounts derived from per-order storage.mint(address,uint256) with selector 0x40c10f19 to mint rewards to the order owner.Reward token implementation (0x28221c8...)
0x28221c8... is an ERC20-like token with 9 decimals and events such as Transfer, Approval, RewardMint, AuthorizedMinterSet, MarketplaceSet, and OwnershipTransferred.mint(address,uint256) function is gated so that only marketplace may mint, and setMarketplace(address) is owner-only. The deployment and configuration transactions set marketplace = 0xb7e1..., meaning only the marketplace proxy can mint reward tokens./// From collected reward-token decompiled source (implementation 0x28221c8...)
/// @custom:selector 0x40c10f19
/// @custom:signature mint(address arg0, uint256 arg1) public
function mint(address arg0, uint256 arg1) public {
require(arg0 == (address(arg0)));
require(msg.sender == (address(marketplace)), CustomError_e450d38c());
// ...
}
Snippet 1 – Reward token mint gate: only the configured marketplace (here proxy 0xb7e1...) can mint 0x7570... tokens.
0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae (USDT/WBNB).0x10ed43c718714eb63d5aa57b78b54704e256024e.The marketplace implementation 0x0df9d2... is designed to manage per-user orders. Functions such as cancelOrder and editOrder:
msg.sender matches the recorded order owner.transfer / transferFrom.mint function.These functions therefore maintain a clear relationship between user actions, order state, and token balances.
By contrast, the implementation exposes another entry point at selector 0x9b3e9b92, surfaced via the proxy 0xb7e1.... Decompiled metadata confirms its presence:
/// From collected marketplace decompiled source (implementation 0x0df9d2...)
/// @custom:selector 0x9b3e9b92
/// @custom:signature Unresolved_9b3e9b92(address arg0, uint256 arg1, uint256 arg2, uint256 arg3, uint256 arg4, uint256 arg5) public pure
function Unresolved_9b3e9b92(address arg0, uint256 arg1, uint256 arg2, uint256 arg3, uint256 arg4, uint256 arg5) public pure {
require(arg0 == (address(arg0)));
require(arg3 == arg3);
require(!arg4 > 0xffffffffffffffff);
require(!(arg4) > 0xffffffffffffffff);
require(!arg5 > 0xffffffffffffffff);
}
Snippet 2 – Marketplace decompiled metadata showing a public function at selector 0x9b3e9b92. The decompiler does not recover its real effects, so we rely on the on-chain trace to understand its behavior.
The decompiler’s “pure” stub is clearly inconsistent with the observed side effects in the exploit transaction. To accurately characterize the function, we must rely on the concrete trace of the seed transaction, which calls 0x9b3e9b92 through the proxy.
The seed transaction 0x864d33d0... is a contract-creation transaction from EOA 0x9f2e.... The constructor of the created helper contract 0x9b78... performs the exploit by:
0xb7e1... with selector 0x9b3e9b92.0x0df9d2... to:
0x55d3... and reward token 0x7570....balanceOf(0xb7e1...).This behavior is visible in the seed transaction’s balance diff and ERC20 transfers:
{
"chainid": 56,
"txhash": "0x864d33d006e5c39c9ee8b35be5ae05a2013e556be3e078e2881b0cc6281bb265",
"native_balance_deltas": [
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"delta_wei": "-13041098705767818172"
},
{
"address": "0x9f2ecec0145242c094b17807f299ce552a625ac5",
"before_wei": "98371945900000000",
"after_wei": "13137453130667818172",
"delta_wei": "13039081184767818172"
}
],
"erc20_transfers": [
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"from": "0xb7e1d1372f2880373d7c5a931cdbaa73c38663c6",
"to": "0x9b78b5d9febce2b8868ea6ee2822cb482a85ad74",
"value": "8484920000000000000000"
},
{
"token": "0x7570fdad10010a06712cae03d2fc2b3a53640aa4",
"from": "0x0000000000000000000000000000000000000000",
"to": "0x9b78b5d9febce2b8868ea6ee2822cb482a85ad74",
"value": "16969840000000000000000"
}
]
}
Snippet 3 – Seed transaction balance diff (balance_diff.json): the marketplace proxy 0xb7e1... sends 8,484.92 USDT (1e18 units) to helper 0x9b78..., and the reward token mints 16,969.84 × 2 = 33,939.68 units to the same helper. The attacker EOA’s BNB balance increases by ~13.039 BNB, while the WBNB contract balance decreases by ~13.041 WBNB.
Key points from the trace and balance diff:
0x55d3... flows:
0xb7e1... to helper 0x9b78... for 8,484.92 USDT.0x9b78... to Pancake pair 0x16b9... for the same 8,484.92 USDT.0x7570... mints:
0x9b78..., twice, totaling 33,939.68 units.0xbb4c... loses ~13.041 BNB-equivalent.0x9f2e... gains ~13.039 BNB net after gas.The call trace (callTracer) confirms that this sequence is triggered by a single call through 0xb7e1... with selector 0x9b3e9b92 originating from the helper’s constructor.
From the combination of:
0x9b3e9b92 exposed via proxy 0xb7e1....mint to only the marketplace address.0x9b3e9b92 to:
we conclude:
0x9b3e9b92 is not restricted to an owner, operator, or authenticated actor; any unprivileged caller can trigger it via the proxy.balanceOf(proxy) and mints rewards based solely on that balance, ignoring per-user deposits and order state.0xb7e1... is a TransparentUpgradeableProxy with no additional gating, this implementation bug directly exposes user funds held by the proxy.This combination creates a straightforward exploit predicate: whenever the proxy holds USDT, any address capable of sending a transaction can drain the entire balance and mint associated rewards, then liquidate for profit.
EOA 0x9f2ecec0145242c094b17807f299ce552a625ac5 (attacker origin)
0x864d33d0....0x0d5550d52428e7e3175bfc9550207e4ad3859b17.Helper contract 0x9b78b5d9febce2b8868ea6ee2822cb482a85ad74
0xb7e1... with selector 0x9b3e9b92 to:
Victim contracts
0xb7e1d1372f2880373d7c5a931cdbaa73c38663c6:
0x9b3e9b92 function.0x7570fdad10010a06712cae03d2fc2b3a53640aa4:
0xb7e1....The exploit is executed entirely within a single transaction:
{
"hash": "0x864d33d006e5c39c9ee8b35be5ae05a2013e556be3e078e2881b0cc6281bb265",
"from": "0x9f2ecec0145242c094b17807f299ce552a625ac5",
"to": "",
"value": "0",
"functionName": "atInversebrah(int248 a, uint48[] b, uint32 c, bytes20[] d, bytes30[] e)"
}
Snippet 4 – Seed transaction summary from the attacker’s txlist: an EOA contract-creation transaction whose constructor performs the exploit logic.
Lifecycle within this transaction:
Helper deployment
0x9f2e... sends a contract-creation transaction with zero BNB value.0x9b78... (nonce 0 from the attacker EOA).Exploit call from constructor
0xb7e1... with selector 0x9b3e9b92.0x0df9d2..., triggering the vulnerable logic described above.0xb7e1... to 0x9b78....0x9b78... via proxy 0x7570....Swap to BNB
0x9b78... swaps the stolen USDT into WBNB via Pancake pair 0x16b9....0x9f2e....native_balance_deltas) is:
0xbb4c... loses ~13.041 BNB-equivalent.0x9f2e... gains ~13.039 BNB after gas.After realizing the profit, the attacker EOA disperses the BNB into a Tornado-like mixer:
[
{
"hash": "0x3a34200e5db502a2593d3cfcf595e477ea2f351c467626f8d2b7be7a717be35e",
"from": "0x9f2ecec0145242c094b17807f299ce552a625ac5",
"to": "0x0d5550d52428e7e3175bfc9550207e4ad3859b17",
"value": "10000000000000000000",
"functionName": "deposit(address _tornado, bytes32 _commitment, bytes _encryptedNote)"
},
{
"hash": "0x15395547c4ccf60829b54c9ac323380ad5e55e762a84b360d987fff986c61c44",
"from": "0x9f2ecec0145242c094b17807f299ce552a625ac5",
"to": "0x0d5550d52428e7e3175bfc9550207e4ad3859b17",
"value": "1000000000000000000",
"functionName": "deposit(address _tornado, bytes32 _commitment, bytes _encryptedNote)"
}
]
Snippet 5 – Example mixer deposits from the attacker’s txlist: 10 BNB and 1 BNB transfers to a Tornado-like mixer contract via deposit(...). Additional 1 BNB and 0.1 BNB deposits follow in later transactions.
These transactions confirm that:
0x9f2e...) controls the profits.Prior to the exploit:
0xbeb28a0... deploys the reward-token and marketplace proxies (0x7570... and 0xb7e1...).marketplace = 0xb7e1... on the reward token, allowing it to mint tokens.opWallet on 0xb7e1..., used in a zero-value USDT transfer observed in the exploit trace.This setup confirms that the vulnerable marketplace and its reward token were configured as intended before an unrelated EOA exploited the exposed function.
USDT loss
0xb7e1... loses its entire on-chain USDT balance of 8,484.92 units to the helper contract 0x9b78... in the exploit transaction.Reward token minting
0x7570... are minted from the zero address to helper 0x9b78....0xb7e1... is configured as marketplace on the reward token contract.BNB profit
0x9f2e... increases its BNB balance from 0.0983719459 BNB to 13.137453130667818172 BNB.13.039081184767818172 BNB after gas.0xbb4c... loses 13.041098705767818172 WBNB-equivalent, consistent with AMM swap outputs and gas costs.[1] Seed transaction call trace (callTracer)
debug_traceTransaction with callTracer for 0x864d33d0..., showing the helper’s constructor call into 0xb7e1...::0x9b3e9b92, USDT and reward-token calls, and the AMM swap path.[2] Seed transaction pre-state (prestateTracer)
[3] Marketplace implementation decompiled source
0x0df9d2..., including the protected order functions and the unresolved 0x9b3e9b92 entry point.[4] Reward token implementation decompiled source
0x28221c8..., including mint(address,uint256) and setMarketplace(address) which demonstrate the marketplace’s authority to mint tokens.[5] Seed transaction balance diff
balance_diff.json for the seed transaction, used to quantify USDT drain and BNB profit.[6] Creator EOA deployment and configuration txlist
0xbeb28a0..., showing deployment of the marketplace and reward-token proxies and subsequent configuration calls.[7] Attacker EOA txlist
0x9f2e..., including the exploit transaction and subsequent mixer deposits used to obfuscate stolen funds.