October 20, 2026
|
Exploit Postmortem
Cover image for "BonfireSwap's $47K Loss and How Olympix Would Have Prevented It"

BonfireSwap's $47K Loss and How Olympix Would Have Prevented It

Date: September 15, 2026

Chain: BNB Chain

Loss: about $47K, the 66.08 WBNB the stolen BONFIRE was sold for

Class: arbitrary-sender transfer that spent other people's approvals

Over time, dozens of holders of the BONFIRE token on BNB Chain each approved the BonfireSwap router to spend their tokens, with no limit, the way most people approve a router before they trade. In one transaction, an attacker used those approvals to empty 41 of those wallets without any of their owners signing a thing.

The attacker didn't need anyone's keys. The router's transfer function let the caller name any address as the source of the tokens, then spent from it using the allowance that address had given the router. It never checked that the caller was that address or had been authorized by it, so the attacker named each victim in turn, collected their BONFIRE and sold the haul for 66.08 WBNB, about $47K.

A Fee Token and Its Router

BONFIRE is a BEP-20 token on BNB Chain that its project site describes as a frictionless, yield-generating contract. Every transaction carries a 10% tax, with 5% distributed to holders and 5% added to liquidity, and the token caps any single transaction at 5,000 tokens. BonfireSwap's router sat alongside the token and moved BONFIRE in and out of its PancakeSwap pair for users.

Like any router, it needed permission to move tokens out of a user's wallet, so users granted it an ERC-20 allowance. An allowance is a standing permission recorded in the token contract: the holder says this router may spend up to some amount of my tokens. According to the DeFiHackLabs proof of concept, every real victim had given the router an unlimited allowance during earlier, legitimate use.

A Transfer Function That Never Asked Who Was Calling

The vulnerable function was transfer(address to, uint amountAIn, address beneficiary, uint deadline) in the router. Its first argument, called to, was actually the wallet the tokens would come from. DeFiHackLabs, reading the router's verified source, shows the body: it pulled amountAIn of BONFIRE from that wallet into the PancakeSwap pair through an internal _safeTransferFrom, then called skimPools(beneficiary) to sweep the pair's excess to whoever the caller named as beneficiary. SlowMist's analysis, as reported by KuCoin and ChainCatcher, identifies what was missing: the function didn't check whether the caller was that wallet, or whether that wallet had authorized the caller to spend its assets. DeFiHackLabs adds that two sibling functions, simpleTransfer() and loggedTransfer(), shared the same pattern.

ERC-20's own transferFrom solves this with a two-party rule. A spender can move tokens out of a wallet only if the wallet has given that spender an allowance, and the allowance shrinks as it's used. A router that spends on a user's behalf has to recreate that rule for its own callers, because the token only knows the router has permission, and it can't tell which caller the router is acting for. BonfireSwap's router held the permission and lent it to whoever called.

Think of a valet stand that will bring around any car whose ticket number you read out, without checking you hold the ticket. Each owner trusted the valet with the keys, and the valet never asked who was collecting. When a contract holds approvals from many users, the identity of the person spending them has to be checked inside that contract, because the token's allowance only says which contract may spend.

Forty-One Wallets in One Transaction

The exploit ran at 09:15:32 UTC on September 15, 2026, in transaction 0xb4c0...193f in block 122,003,954, sent from attacker address 0x2B5b...731a through an attack contract at 0x28E9...2127. The contract called the router's transfer function once per victim, each time naming the holder as the source and itself as the beneficiary. Because the pair was the router's own pool, each skim handed the victim's tokens straight back to the attack contract as BONFIRE. The 5,000-token transaction cap meant the largest victim, who lost 5,289.10 BONFIRE, had to be drained in two chunks, and across 61 skims the attacker collected 5,082.49 BONFIRE.

The attacker then cashed out through the router's own sell function in two sells, for 33.22 and 32.86 WBNB. DeFiHackLabs notes that early alerts described WBNB leaving each victim's wallet, and that the trace shows the victims' tokens arriving as BONFIRE and only the attacker's sells producing WBNB. A separate skimPool call the attacker also made reverted inside a try/catch and realized nothing. SlowMist's alert went out at about 03:30 UTC on September 16, roughly 18 hours after the drain.

The proof of concept, which drains only the five largest victims, reproduces 58.78 BNB, about 89% of the 66.08 WBNB realized on-chain, because those five held most of the BONFIRE taken. It also reconciles the victim counts: the transfer logs contain 67 distinct senders, including the attack contract and the token contract itself, which leaves about 65 real external holders, about 41 of whom had balances worth draining. That's why Defimon reported about 65 holders and SlowMist reported 41.

The Price Tag Across Sources

SlowMist put the loss at about $50,000 and the number of affected holders at 41. DefiLlama lists $47,400 under access control. Defimon's alert reported about 66 BNB, roughly $47,000, across about 65 holders. The two WBNB sells in the exploit transaction add up to 66.08 WBNB, which lines up with Defimon's BNB figure. At CoinGecko's BNB price across September 15 and 16, which moved between about $709 and $723, that's $46.8K to $47.8K, so this post uses about $47K, in line with DefiLlama's $47,400.

How Olympix Traces a Caller-Supplied Address Into a Token Transfer

Olympix reads a contract the way an attacker does, as states, transitions and entry points, and it takes in the context around the code, here the allowances users grant a router so it can trade for them, to infer the invariants the system depends on but never writes down. For this router the invariant is one sentence: no caller can reduce another account's token balance unless that account is the caller or has authorized the caller.

The engine maps every public entry into the router and uses signals to decide which to attack. Olympix's static analysis surfaces the signal here: an address taken straight from a function argument flows into a transferFrom against the router's allowance, with no check that it equals msg.sender and no allowance of the caller's own consumed on the way. The same signal lights up three times in this router, on transfer, simpleTransfer and loggedTransfer. Olympix has flagged this arbitrary-sender class before in other codebases, in a router that passed an unvalidated source address into safeTransferFrom.

From those flagged entries, BugPoCer runs Counterexample-Guided Invariant Refutation against the whole router and token pair. It derives the balance rule from how BONFIRE's allowances and the router's spending fit together, then drives calls from addresses that hold no permission at all. A fresh address calling transfer with a holder's wallet as the first argument, and itself as the beneficiary, breaks the rule on the first try.

The output is a counterexample: an arbitrary caller names a victim as the source, the router spends the victim's allowance, and the victim's BONFIRE lands with the caller. BugPoCer returns that sequence with the minimal conditions that trigger it and a generated Forge test that replays it, which is the September drain produced in development instead of on BNB Chain. Because Olympix runs in development, at every merge and before release, a router function that takes an address as its funding source gets that check the day it's written.

Approvals Are Only as Safe as the Router

The 41 BONFIRE holders did exactly what routers ask users to do, and the router turned their approvals into a public spending account. Any contract that holds standing approvals needs every function that spends them to prove the caller owns, or has been authorized for, the wallet being spent. To see how Olympix tests the approval paths in your own contracts and hands back a runnable counterexample for any that let one user spend another's tokens, book a technical walkthrough.

On-Chain References

Protocol: BonfireSwap router (BONFIRE token)

Chain: BNB Chain

Date: September 15, 2026, 09:15:32 UTC (block timestamp)

Loss: 66.08 WBNB (33.22 + 32.86 from the attacker's two sells), about $47K at CoinGecko's September 15 to 16 BNB price; SlowMist's alert said about $50K

Attacker EOA: 0x2B5b...731a

Attack contract: 0x28E9...2127

Vulnerable router (verified): 0x17e8...03D3

BONFIRE token: 0x5e90...5590

BONFIRE/WBNB PancakeSwap pair: 0xD3F4...38f2

Exploit tx: 0xb4c0...193f

Block: 122003954

Sources

SlowMist: BonfireSwap router exploit alert

KuCoin (ChainCatcher): BonfireSwap Router Vulnerability Leads to $50K Loss, 41 Users Affected

DeFiHackLabs: BonfireSwap exploit proof of concept

Defimon Alerts: BonfireSwap drain alert

Bonfire: Official project site

More from Olympix:

No items found.

Ready to Shift Security Assurance In-House? Talk to Our Security Experts Today.