We do not have a reliable USD price for the recorded assets yet.
0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f0xfd0b4daa7ba535741e6b5ba28cba24f9a816e67eEthereum0x5f58058c0ec971492166763c8c22632b583f667fEthereumOn Ethereum mainnet block 10307564, an unprivileged adversary exploited BancorNetwork at 0x5f58058c0ec971492166763c8c22632b583f667f to drain 905987977635678910008152 XBP from victim 0xfd0b4daa7ba535741e6b5ba28cba24f9a816e67e in transaction 0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f. The attacker did not need any privileged role, private signature, or custom attacker contract. The transaction simply called BancorNetwork’s public safeTransferFrom helper and redirected the victim’s already-approved XBP to adversary beneficiary 0x29551fc9c39a95d8f93ad4209944ff05846a05f3.
The root cause is straightforward: BancorNetwork exposed an unrestricted public wrapper around ERC20 transferFrom. Because users had already granted allowances to BancorNetwork for legitimate swap flows, any third party could force BancorNetwork to spend those allowances on their behalf and send the tokens anywhere they chose.
BancorNetwork’s conversion flow relied on users granting ERC20 allowances to the BancorNetwork contract. That pattern is common for on-chain routers: the protocol contract becomes the spender, and later internal swap logic pulls user funds with transferFrom.
The critical ERC20 rule is that transferFrom authorizes msg.sender, not the external caller’s intent. If BancorNetwork is the on-chain caller into the token contract, then the token checks allowance[user][BancorNetwork]. Any public protocol helper that forwards arbitrary and parameters into ERC20 therefore becomes dangerous unless the helper also authenticates who is allowed to trigger that spend.
_from_totransferFromThe victim token here is XBP at 0x28dee01d53fed0edf5f6e310bf8ef9311513ae40. The seed pre-state shows the victim held exactly 905987977635678910008152 XBP and had already granted BancorNetwork an allowance of 30517969405419767184741663587441118 XBP, far exceeding the stolen amount.
This is an access-control failure on a token-spending helper. BancorNetwork exposed safeTransferFrom as a public function and did not bind _from to msg.sender, to an approved swap context, or to any authorized execution path. As a result, the function let arbitrary callers instruct BancorNetwork to spend third-party allowances already granted to the protocol.
The violated invariant is: only the token holder, or a protocol action explicitly authorized by that holder for that specific flow, should be able to decide when BancorNetwork spends the holder’s allowance and where the transferred tokens go. The code-level breakpoint is BancorNetwork safeTransferFrom line 547 in the verified source snapshot, where attacker-controlled _from, _to, and _value are forwarded directly into ERC20 transferFrom via execute(...).
The XBP token behaves as a standard allowance-based ERC20. Its transferFrom implementation debits allowance[_from][msg.sender], decreases the victim balance, and credits the recipient. Because msg.sender at the token layer is BancorNetwork, any public call path that causes BancorNetwork to invoke transferFrom can spend the victim’s existing approval. The exploit therefore required only three live conditions: a victim approval to BancorNetwork, a sufficient victim balance, and the public BancorNetwork helper.
The vulnerable BancorNetwork helper is visible in the collected verified source:
function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) public {
execute(_token, abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
}
There is no access control, no caller binding, and no requirement that the call occur inside an authenticated swap flow. The helper simply encodes arbitrary attacker-supplied parameters and forwards them to the token contract.
The token-side behavior is equally clear in the collected XBP source:
function transferFrom(address _from, address _to, uint256 _value)
public
validParamData(3)
validAddress(_from)
validAddress(_to)
notThis(_to)
returns (bool success)
{
allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
balanceOf[_from] = balanceOf[_from].sub(_value);
balanceOf[_to] = balanceOf[_to].add(_value);
Transfer(_from, _to, _value);
return true;
}
At runtime, the token sees msg.sender == BancorNetwork, so it spends allowance[victim][BancorNetwork]. The seed trace confirms the exact call chain:
BancorNetwork::safeTransferFrom(
XBPToken: [0x28dee01D53FED0Edf5f6E310BF8Ef9311513Ae40],
0xfd0B4DAa7bA535741E6B5Ba28Cba24F9a816E67E,
0x29551FC9C39A95d8f93Ad4209944fF05846a05F3,
905987977635678910008152
)
├─ XBPToken::transferFrom(
0xfd0B4DAa7bA535741E6B5Ba28Cba24F9a816E67E,
0x29551FC9C39A95d8f93Ad4209944fF05846a05F3,
905987977635678910008152
)
The seed transaction calldata in metadata encodes the same four parameters: token 0x28dee01d53fed0edf5f6e310bf8ef9311513ae40, victim 0xfd0b4daa7ba535741e6b5ba28cba24f9a816e67e, beneficiary 0x29551fc9c39a95d8f93ad4209944ff05846a05f3, and amount 905987977635678910008152. Nothing in the call depends on private information or privileged coordination. Any unprivileged EOA that observed the victim’s allowance and balance could submit the same transaction.
The exploit flow is a single-transaction allowance drain. First, EOA 0x14fa61fd261ab950b9ce07685180a9555ab5d665 acquired ETH for gas in earlier funding activity. Second, the adversary submitted transaction 0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f directly to BancorNetwork. The call targeted the public safeTransferFrom helper and specified the victim address as _from, the adversary beneficiary as _to, and the victim’s full XBP balance as _value.
Once BancorNetwork executed the helper, XBPToken consumed the victim’s pre-existing Bancor allowance and transferred the full amount to beneficiary 0x29551fc9c39a95d8f93ad4209944ff05846a05f3. The victim side of the state change is deterministic: the victim balance dropped from 905987977635678910008152 to 0, and BancorNetwork’s allowance against that victim decreased by the same amount. The attacker cluster paid only ETH gas; the balance-diff artifact records -2761800000000000 wei for the exploit sender.
The beneficiary address later spent the stolen XBP in subsequent transfer activity, which confirms post-exploit control of the proceeds rather than a transient routing artifact. The broader adversary pattern described in the analysis is therefore consistent with repeated harvesting of open Bancor allowances across multiple victims and tokens.
The directly measured loss in the validated seed transaction is the victim’s entire XBP balance at execution time:
{
"token_symbol": "XBP",
"amount": "905987977635678910008152",
"decimal": 18
}
In display units, that is 905987.977635678910008152 XBP. The impact extends beyond this single victim because the same flaw applied to any ERC20 holder who had granted BancorNetwork allowance. The protocol contract effectively became a permissionless spender for all outstanding user approvals, turning routine swap approvals into a generalized theft surface.
0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f0x5f58058c0ec971492166763c8c22632b583f667f0x28dee01d53fed0edf5f6e310bf8ef9311513ae400xfd0b4daa7ba535741e6b5ba28cba24f9a816e67e0x14fa61fd261ab950b9ce07685180a9555ab5d6650x29551fc9c39a95d8f93ad4209944ff05846a05f3/workspace/session/artifacts/collector/seed/1/0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f/metadata.json/workspace/session/artifacts/collector/seed/1/0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f/trace.cast.log/workspace/session/artifacts/collector/seed/1/0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f/balance_diff.json/workspace/session/artifacts/auditor/iter_0/bancor_network_source.sol/workspace/session/artifacts/collector/seed/1/0x28dee01d53fed0edf5f6e310bf8ef9311513ae40/src/Contract.sol