# Bebop: arbitrary interaction in settle drains USDC via transferFrom

Bebop settlement contract's settle() executed arbitrary interactions, letting attacker call USDC transferFrom from victims to themselves, stealing ~21k USD.

- Payout: Real-world hack
- Severity: high
- Program: Bebop
- Credited: DeFiHackLabs
- Disclosed: 2025-08-12
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-08/Bebop_dex_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250812-bebop-dex
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity vuln:access-control vuln:business-logic

**Target:** JamSettlement contract on Arbitrum
**Root cause:** The settle() function accepts an array of JamInteraction structs and executes arbitrary calls to any address with any data, without validating that interactions are limited to the order's tokens or that the caller is authorized.
**Steps:**
1. Construct a JamOrder with empty sellTokens/buyTokens/sellAmounts/buyAmounts, taker/receiver/executor = attacker, expiry in future, nonce=1, usingPermit2=false.
2. Set signature to empty bytes.
3. Build two JamInteraction entries: to=USDC (0xaf88...), data=abi.encodeCall(IERC20.transferFrom, (victim1, attacker, amount1)) and similarly for victim2.
4. Call jamContract.settle(order, signature, interactions, emptyHooksData, attacker).
5. Contract executes each interaction, calling USDC.transferFrom(victim, attacker, amount) which succeeds if victims had approved the settlement contract.
**Exploitation:** Attacker calls settle() with crafted interactions that invoke USDC.transferFrom on behalf of victims who had approved the Bebop settlement contract, transferring their USDC to the attacker. No capital or flash loan needed; relies on existing allowances.
**Insight:** When a settlement contract executes arbitrary 'interactions', check if it validates that the target and calldata are constrained to the order's tokens and amounts; otherwise any approved token can be drained via transferFrom.
