# BonfireSwap: missing caller check in transfer() drains approved tokens

BonfireSwap router's transfer() lacked caller authorization, letting anyone force-sell any holder's approved tokens and steal ~66 BNB.

- Payout: Real-world hack
- Severity: critical
- Program: BonfireSwap
- Disclosed: 2026-09-15
- Write-up quality: 8/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-09/BonfireSwap_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260915-bonfireswap
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** BonfireSwap router contract on BSC
**Root cause:** The transfer() function calls _safeTransferFrom(tokenAddress, to, pancakePair, amountAIn) without verifying msg.sender == to, allowing anyone to spend a victim's standing allowance to the router.
**Steps:**
1. Identify holders with a standing allowance to the BonfireSwap router (e.g., from prior legitimate use).
2. Call `ROUTER.transfer(victim, amount, attackerContract, block.timestamp)` for each victim; the router pulls victim's BONFIRE via their allowance and skims it to the attacker's beneficiary address.
3. Approve the router to spend the collected BONFIRE.
4. Call `ROUTER.sell(chunk, 0, attackerContract, block.timestamp)` in chunks under the token's maxTx cap to convert BONFIRE to BNB.
**How found:** Likely discovered by reviewing the verified router source and noticing that transfer() lacks a msg.sender check while pulling tokens from an arbitrary `to` address using that address's allowance.
**Exploitation:** Attacker deployed a contract that looped over victims, calling transfer() with each victim's address as `to` and the attacker's contract as `beneficiary`. The router pulled each victim's tokens using their standing allowance and skimmed them to the attacker. The attacker then sold the accumulated BONFIRE through the router's sell() function, receiving ~66 BNB.
**Insight:** When auditing DeFi routers, check every function that calls transferFrom: ensure it validates that msg.sender is the `from` address or has explicit approval, otherwise anyone can spend standing allowances.
