Orion redeemAtomic exploit drains BNB and token reserves
Exploit Transactions
0x660837a1640dd9cc0561ab7ff6c85325edebfa17d8b11a3bb94457ba6dcae18cVictim Addresses
0xe9d1d2a27458378dd6c6f0b2c390807aed2217caBSCLoss Breakdown
Similar Incidents
BNB-chain constructor exploit drains full USDT pool balance
36%Public mint flaw drains USDT from c3b1 token pool
36%AST liquidity-tracking flaw burns AST reserves and yields BNB profit
36%Mosca double-withdrawal exploit via helper on BNB
36%SlurpyCoin BuyOrSell flaw drains BNB via flash-loan swaps
35%AI IPC destroy-sync mechanism drains IPC-USDT pair USDT reserves
34%Root Cause Analysis
Orion redeemAtomic exploit drains BNB and token reserves
1. Incident Overview TL;DR
On BNB Chain block 39107494, an unprivileged adversary cluster exploited Orion Exchange's BNB Chain deployment (AdminUpgradeabilityProxy at 0xe9d1d2a27458378dd6c6f0b2c390807aed2217ca) to drain BNB and multiple ERC20 tokens in a single transaction 0x660837a1640dd9cc0561ab7ff6c85325edebfa17d8b11a3bb94457ba6dcae18c. The attacker used an external orchestrator contract to obtain a USDT flash swap, deposit into Orion, fabricate large internal balances via redeemAtomic (delegating to LibAtomic.doRedeemAtomic), and then withdraw real assets via withdrawTo while remaining superficially margin-positive.
The root cause is that Orion's redeemAtomic flow allows signed RedeemOrders to credit internal balances without binding them to concrete atomicSwaps lock entries or real reserve-backed positions. As long as MarginalFunctionality.calcPosition reports sufficient margin, Exchange.withdrawTo will release proxy-held tokens against these synthetic balances. This breaks the intended invariant that withdrawals must be limited by actual collateral and locked obligations, enabling an anyone-can-take (ACT) opportunity.
2. Key Background
Orion Exchange on BNB Chain is deployed behind an AdminUpgradeabilityProxy at 0xe9d1d2a27458378dd6c6f0b2c390807aed2217ca. At block 39107494, its implementation is an Orion exchange contract that maintains a per-user internal ledger assetBalances[user][asset] and supports:
depositAssetTo: credit internal balances when users or contracts deposit BNB/ERC20 tokens.withdrawTo: debit internal balances (subject to margin checks) and transfer actual tokens from the proxy.redeemAtomic: implement atomic redeem flows usingLibAtomicandatomicSwapsstorage.
The relevant implementation code is drawn from the verified Orion project cloned during data collection (e.g., Exchange.sol, ExchangeWithAtomic.sol, LibAtomic.sol). In that project, ExchangeWithAtomic.redeemAtomic delegates to LibAtomic.doRedeemAtomic to adjust internal balances based on a signed RedeemOrder:
// ExchangeWithAtomic.sol (simplified call site)
function redeemAtomic(LibAtomic.RedeemOrder calldata order, bytes calldata secret) external {
LibAtomic.doRedeemAtomic(order, secret, secrets, assetBalances, liabilities);
}
The core logic of LibAtomic.doRedeemAtomic is:
// LibAtomic.doRedeemAtomic (key lines)
function doRedeemAtomic(
LibAtomic.RedeemOrder calldata order,
bytes calldata secret,
mapping(bytes32 => bool) storage secrets,
mapping(address => mapping(address => int192)) storage assetBalances,
mapping(address => MarginalFunctionality.Liability[]) storage liabilities
) public {
require(!secrets[order.secretHash], "E17R");
require(getEthSignedAtomicOrderHash(order).recover(order.signature) == order.sender, "E2");
require(order.expiration / 1000 >= block.timestamp, "E4A");
require(order.secretHash == keccak256(secret), "E17");
secrets[order.secretHash] = true;
LibExchange._updateBalance(order.sender, order.asset, -1 * int(uint(order.amount)), assetBalances, liabilities);
LibExchange._updateBalance(order.receiver, order.asset, int(uint(order.amount)), assetBalances, liabilities);
}
Critically, this function only verifies the redeem order signature, expiration, and matching secret hash. It does not reference any atomicSwaps lock entry or ensure that order.amount is backed by an on-chain lock or by existing reserves; it simply debits and credits internal balances for order.sender and order.receiver.
Withdrawals are handled in Exchange.withdrawTo:
// Exchange.withdrawTo (simplified)
function withdrawTo(address assetAddress, uint112 amount, address to) public nonReentrant {
uint112 safeAmountDecimal = LibUnitConverter.baseUnitToDecimal(assetAddress, amount);
assetBalances[msg.sender][assetAddress] -= int192(uint192(safeAmountDecimal));
emit LibExchange.BalanceChange(msg.sender, assetAddress, -int192(uint192(safeAmountDecimal)));
if (assetBalances[msg.sender][assetAddress] < 0) revert NotEnoughBalance();
if (!checkPosition(msg.sender)) revert IncorrectPosition();
if (assetAddress == address(0)) {
(bool success, ) = to.call{value: amount}("");
if (!success) revert NotEnoughBalance();
} else {
IERC20(assetAddress).safeTransfer(to, amount);
}
emit NewAssetTransaction(msg.sender, to, assetAddress, false, safeAmountDecimal, uint64(block.timestamp));
}
As long as assetBalances[msg.sender][assetAddress] remains non-negative and checkPosition(msg.sender) returns true, the contract will transfer real BNB or tokens held by the proxy to the to address.
The adversary model is a standard, unprivileged on-chain attacker that controls multiple EOAs and contracts, can obtain flash liquidity (e.g., via a USDT flash swap), and interacts only with publicly available Orion functions and on-chain information.
3. Vulnerability Analysis & Root Cause Summary
The vulnerability is an internal accounting flaw in Orion's atomic redeem / withdrawal pipeline. LibAtomic.doRedeemAtomic creates internal balances for a receiver purely from a signed RedeemOrder and secret, without tying that balance creation to any actual atomicSwaps lock or reserve-backed obligation. The subsequent withdrawTo function trusts these internal balances (subject only to a portfolio-wide margin check) and releases real assets from the proxy.
The intended invariant is that, for every account, the combination of deposits, locks, and liabilities must ensure that withdrawals cannot exceed the assets genuinely backing that account's position. In the exploited implementation, the invariant is broken because a crafted sequence of RedeemOrders can move large notional amounts into attacker-controlled internal balances without consuming or checking corresponding locks.
An unprivileged adversary can therefore:
- Use a flash swap to obtain temporary liquidity.
- Deposit a portion into Orion.
- Call
redeemAtomicrepeatedly to fabricate internal balances across multiple assets while preserving margin. - Call
withdrawToto drain BNB and ERC20 tokens from the proxy to an adversary EOA. - Repay the flash swap and keep the residual multi-asset profit.
This behavior is an ACT opportunity because any unprivileged address or cluster can reproduce the same strategy using public code, traces, and price data under the same pre-state.
4. Detailed Root Cause Analysis
4.1 Safety invariant and breakpoint
The safety invariant can be expressed as:
For every user
uand asseta, the exchange should ensure that withdrawals and internal balance credits are bounded by the combination of deposits and lock-backed obligations, such that the total claim on reserves does not exceed actual reserves.
Formally, for each user u and asset a over time:
internal_balance[u,a] <= deposits[u,a] + lock_claims[u,a] - liabilities[u,a]
and the sum over all users should not exceed the proxy's external holdings for a.
The code-level breakpoint is in LibAtomic.doRedeemAtomic and ExchangeWithAtomic.redeemAtomic:
redeemAtomicinvokesLibAtomic.doRedeemAtomic(order, secret, secrets, assetBalances, liabilities).doRedeemAtomicverifies only the signature and secret, then calls_updateBalanceto debitorder.senderand creditorder.receiverfororder.assetandorder.amount.- There is no linkage between
orderand anyatomicSwaps[secretHash]entry; no check that a corresponding lock exists, is unused, or matchesorder.amountandorder.asset.
As a result, the attacker can construct RedeemOrders where order.sender is an internal/broker account and order.receiver is an attacker-controlled account, with large order.amount values, and apply them repeatedly as long as the global margin check remains satisfied.
On the withdrawal side, Exchange.withdrawTo relies on assetBalances[msg.sender][assetAddress] and checkPosition(msg.sender) to prevent over-withdrawal. However, because the internal balances were already inflated by doRedeemAtomic without consuming real locks, these checks operate on corrupted accounting state and permit withdrawals backed only by fabricated balances.
4.2 Adversary-controlled inputs and reachability
The adversary controls:
- EOA
0x51177db1ff3b450007958447946a2eee388288d2(seed tx sender and gas payer). - Orchestrator contract
0xf8bfac82bdd7ac82d3aeec98b9e1e73579509db6that encodes the attack sequence. - Internal/broker account
0x805d8c96d09f6500cdad501d5c754d2fbf186d7aused within Orion accounting. - Profit EOA
0xf7a8c237ac04c1c3361851ea78e8f50b04c76152(receives drained assets).
The orchestrator is an unprivileged contract; any EOA could deploy an equivalent contract and call the same public functions on the Orion proxy. All exploited functions are publicly callable:
depositAssetToon the proxy for depositing USDT.redeemAtomicon the proxy, delegating toLibAtomic.doRedeemAtomicin the implementation.withdrawToon the proxy for withdrawing BNB and tokens.
The seed transaction 0x660837a1640dd9cc0561ab7ff6c85325edebfa17d8b11a3bb94457ba6dcae18c is a standard BNB Chain transaction from an unprivileged EOA to the orchestrator with sufficient gas, demonstrating reachability under the ACT model.
4.3 Trace evidence: attack sequence
The trace for 0x6608...e18c (captured in trace.cast.log) shows the following high-level sequence:
0x5117...d2calls orchestrator0xf8bfac82...with zero BNB value.- The orchestrator initiates a USDT flash swap from
0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae. - Using part of the borrowed USDT, the orchestrator calls
depositAssetToon proxy0xe9d1d2a2...to credit internal USDT balances for attacker-controlled accounts. - The orchestrator then calls
redeemAtomicmultiple times on the proxy, which delegatecalls intoLibAtomic.doRedeemAtomicon implementation0xc662cea3..., creating large internal balances in BNB and multiple ERC20 tokens for attacker-linked accounts while maintaining a margin-positive portfolio according tocalcPosition. - Finally, the orchestrator calls
withdrawTorepeatedly, causing the proxy to transfer real BNB and ERC20 tokens to profit EOA0xf7a8...and to the flash swap provider, then repays the USDT flash swap and leaves the net profit with the adversary.
4.4 Quantitative invariant violation
The balance_diff.json for the seed transaction confirms the invariant violation at the level of the proxy's reserves. For the proxy 0xe9d1d2a2..., native and ERC20 balance deltas include (negative amounts reflect losses by the proxy):
{
"native_balance_deltas": [
{
"address": "0xe9d1d2a27458378dd6c6f0b2c390807aed2217ca",
"delta_wei": "-79896159740000000000"
}
],
"erc20_balance_deltas": [
{"token": "0x55d398326f99059ff775485246999027b3197955", "delta": "-19844686077960000000000"},
{"token": "0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe", "delta": "-62444730331000000000000"},
{"token": "0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd", "delta": "-1741195444560000000000"},
{"token": "0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c", "delta": "-303464280000000000"},
{"token": "0x2170ed0880ac9a755fd29b2688956bd959f933f8", "delta": "-4808971490000000000"},
{"token": "0x7083609fce4d1d8dc0c979aab8c869ea2c873402", "delta": "-785588869650000000000"},
{"token": "0xe4ca1f75eca6214393fce1c1b316c237664eaa8e", "delta": "-49892192920826"},
{"token": "0xba2ae424d960c26247dd6c32edc70b295c744c43", "delta": "-11261078858282"},
{"token": "0xadbaf88b39d37dc68775ed1541f1bf83a5a45feb", "delta": "-86361313198550000000000"},
{"token": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", "delta": "-11001109311140000000000"},
{"token": "0xbf7c81fff98bbe61b40ed186e4afd6ddd01337fe", "delta": "-237884641240000000000"},
{"token": "0x1ce0c2827e2ef14d5c4f29a091d735a204794041", "delta": "-198361026380000000000"}
]
}
These losses at the proxy are mirrored by gains at the profit EOA 0xf7a8..., confirming that the internal accounting allowed the attacker to withdraw more than any legitimate deposits or locks should have permitted.
5. Adversary Flow Analysis
5.1 Adversary-related cluster
The adversary-related cluster of accounts used in this exploit is:
0x51177db1ff3b450007958447946a2eee388288d2— EOA that submits the seed transaction and pays gas.0xf8bfac82bdd7ac82d3aeec98b9e1e73579509db6— attacker-controlled orchestrator contract implementing the flash swap, Orion interaction, and withdrawals.0x805d8c96d09f6500cdad501d5c754d2fbf186d7a— broker/internal account within Orion used to route balances.0xf7a8c237ac04c1c3361851ea78e8f50b04c76152— profit EOA receiving the drained BNB and ERC20 tokens.
All of these are unprivileged accounts under the ACT adversary model. The orchestrator is not access-controlled and could be called by any EOA with sufficient gas.
5.2 Step-by-step on-chain flow (tx 0x6608...e18c)
-
Flash swap and funding
- The orchestrator
0xf8bfac82...initiates a USDT flash swap from0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae. balance_diff.jsonshows0x16b9...temporarily loses10,400USDT (delta-10400000000000000000000at the lender) and later regains it when the flash swap is repaid.
- The orchestrator
-
Deposit into Orion
- Using part of the borrowed USDT, the orchestrator calls
depositAssetToon proxy0xe9d1d2a2..., crediting internal USDT balances for attacker-controlled accounts. - This provides some legitimate collateral and helps keep
calcPositionnon-negative during the exploit.
- Using part of the borrowed USDT, the orchestrator calls
-
RedeemAtomic internal balance fabrication
- The orchestrator calls
redeemAtomicmultiple times with carefully craftedRedeemOrders. - Each call delegates to
LibAtomic.doRedeemAtomic, which:- Verifies the order signature and secret.
- Marks the secret as used.
- Debits
order.senderand creditsorder.receiverinassetBalancesfor various assets (BNB proxy representation and multiple ERC20s).
- Because
doRedeemAtomicdoes not consume or verify locks inatomicSwaps, the attacker can move arbitrarily large nominal amounts into their internal balances, as long as the overall margin check remains satisfied.
- The orchestrator calls
-
Withdrawals and profit realization
- After inflating internal balances, the orchestrator repeatedly calls
withdrawToon the proxy for multiple assets, withmsg.senderset to an attacker-controlled internal account andtoset to the profit EOA0xf7a8...(or the flash swap lender for the repayment). withdrawTosubtractssafeAmountDecimalfrom the internal balance, checksNotEnoughBalanceandIncorrectPosition, and then transfers tokens from the proxy to the target on success.- The trace and
balance_diff.jsonshow:- Proxy
0xe9d1d2a2...loses79.89615974BNB (delta-79896159740000000000wei) and large ERC20 balances across USDT, XRP, LINK, BTCB, ETH, DOT, ORN, DOGE, COTI, USDC, EGLD, and AVAX. - Profit EOA
0xf7a8...gains the corresponding positive deltas for those tokens. - The flash swap lender
0x16b9...receives back10,400USDT. - Tx sender
0x5117...pays approximately0.026438544BNB in gas (native balance delta-26438544000000000wei).
- Proxy
- After inflating internal balances, the orchestrator repeatedly calls
-
Post-tx state
- The flash swap is fully repaid.
- The adversary cluster retains a large multi-asset portfolio at the profit EOA, funded entirely by Orion's proxy reserves.
This flow is reproducible by any unprivileged adversary with knowledge of the vulnerability and access to similar flash liquidity, satisfying the ACT criteria.
6. Impact & Losses
6.1 Token-level losses from Orion proxy
From balance_diff.json, the direct on-chain losses from the Orion proxy 0xe9d1d2a2... in transaction 0x6608...e18c are:
- BNB (native):
79.89615974BNB (delta-79896159740000000000wei). - USDT (
0x55d398326f99059ff775485246999027b3197955):19,844,686.07796USDT-equivalent units (delta-19844686077960000000000with 18 decimals). - XRP (
0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe). - LINK (
0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd). - BTCB (
0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c). - ETH (
0x2170ed0880ac9a755fd29b2688956bd959f933f8). - DOT (
0x7083609fce4d1d8dc0c979aab8c869ea2c873402). - ORN (
0xe4ca1f75eca6214393fce1c1b316c237664eaa8e). - DOGE (
0xba2ae424d960c26247dd6c32edc70b295c744c43). - COTI (
0xadbaf88b39d37dc68775ed1541f1bf83a5a45feb). - USDC (
0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d). - EGLD (
0xbf7c81fff98bbe61b40ed186e4afd6ddd01337fe). - AVAX (
0x1ce0c2827e2ef14d5c4f29a091d735a204794041).
For these ERC20 tokens, the exact integer deltas (in base units) are recorded in balance_diff.json and align with the corresponding positive deltas at the profit EOA 0xf7a8....
6.2 Quantified adversary profit in USD
Using the historical USD prices in artifacts/root_cause/data_collector/iter_8/prices/historical_prices_block_39107494_binance.json, which provide Binance spot 1m close prices (or 1.0 USD pegs for USDT/USDC) at or immediately preceding the block timestamp, the adversary-cluster portfolio change can be valued in USD.
The portfolio is restricted to assets with non-zero deltas in this transaction (BNB, USDT, XRP, LINK, BTCB, ETH, DOT, ORN, DOGE, COTI, USDC, EGLD, AVAX) and to addresses in the adversary cluster {0x5117..., 0xf8bfac82..., 0x805d8c96..., 0xf7a8...}. Token-unit deltas come from balance_diff.json and decimals from verified sources or explorer metadata (BNB and main BEP20s: 18 decimals; ORN and DOGE: 8 decimals; other BEP20TokenImplementation assets treated as 18 decimals).
Under these assumptions:
- The cluster's portfolio value before the transaction is approximately $79.54.
- The cluster's portfolio value after the transaction is approximately $941,043.75.
- The tx sender pays gas of about 0.026438544 BNB, worth ~$15.77 at the BNB price of $596.5.
- The net portfolio gain in USD, after accounting for gas fees, is approximately $940,964.21.
Because BNB gas spending is already included in the cluster's native balance deltas, the reported value_delta_in_reference_asset in the analysis is net of fees; fees are reported separately for clarity.
6.3 ACT opportunity characterization
Given the above, this incident clearly satisfies the ACT profit predicate:
- The adversary-cluster net portfolio value in USD increases strictly after fees.
- All required inputs (contract sources, traces, balance diffs, price data) are publicly obtainable.
- The strategy uses only permissionless, unprivileged interactions with Orion and standard on-chain primitives (flash swap, ERC20 transfers).
Therefore, the root cause is an ACT-style attack: an internal accounting vulnerability in Orion's redeem/withdraw pipeline that any unprivileged adversary can exploit to realize a large, quantified profit.
7. References
- Seed transaction and state diffs: BNB Chain tx
0x660837a1640dd9cc0561ab7ff6c85325edebfa17d8b11a3bb94457ba6dcae18c(block 39107494), with metadata, trace, and balance diffs inartifacts/root_cause/seed/56/0x6608...e18c/. - Victim proxy:
AdminUpgradeabilityProxyat0xe9d1d2a27458378dd6c6f0b2c390807aed2217caon BNB Chain. - Victim implementation sources: Verified Orion implementation project cloned under
artifacts/root_cause/data_collector/iter_4/contract/56/0xe2c7ce00e0bbbcd15c691dfcca7434f3b95f0750/source/(includingExchange.sol,ExchangeWithAtomic.sol,LibAtomic.sol). - Implementation project at exploit block: Verified project for implementation
0xc662cea3d8d6660ca97fb9ff98122da69a199cd8underartifacts/root_cause/data_collector/iter_5/contract/56/0xc662...cd8/source/. - Adversary contracts and accounts: Orchestrator
0xf8bfac82bdd7ac82d3aeec98b9e1e73579509db6, profit EOA0xf7a8c237ac04c1c3361851ea78e8f50b04c76152, broker/internal0x805d8c96d09f6500cdad501d5c754d2fbf186d7a, flash swap lender0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae, with txlists inartifacts/root_cause/data_collector/iter_3/address/56/*/txlist.normal.json. - Historical prices: Binance 1m kline USD prices for BNB and drained tokens at block 39107494 in
artifacts/root_cause/data_collector/iter_8/prices/historical_prices_block_39107494_binance.json.
These references are sufficient for an external reader to independently reconstruct the exploit, verify the invariant violation, and re-compute the adversary's profit in USD.