# Ekubo router: repeated lock drains victim WBTC via pay callback

Ekubo router lock/forward/withdraw/pay callback logic allowed repeated WBTC withdrawals from a victim's balance via 85 lock iterations, stealing ~$1.4M.

- Payout: Real-world hack
- Severity: high
- Program: Ekubo
- Credited: DeFiHackLabs
- Disclosed: 2026-05-05
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-05/Ekubo_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260505-ekubo
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** Ekubo Router / Core lock-forward-withdraw-pay flow
**Root cause:** The router's locked() callback forwards, withdraws, and pays without validating that the pay callback funds the withdrawal from the caller's own balance; payCallback pulls WBTC from a fixed victim address instead of the caller.
**Steps:**
1. Deploy exploit contract with profit receiver.
2. Call `CORE.lock()` 85 times in a loop.
3. In `locked()`, call `CORE.forward(WBTC)`, then `CORE.withdraw(WBTC, PROFIT_RECEIVER, 20_000_000)`, then `CORE.pay(WBTC)`.
4. In `payCallback()`, transfer 20_000_000 WBTC from VICTIM to CORE via `WBTC.transferFrom(VICTIM, CORE, amount)`.
5. Repeat until WBTC balance of profit receiver reaches 1_700_000_000 (17 WBTC).
**Exploitation:** Attacker contract repeatedly invokes Ekubo Core lock, and in the locked callback performs forward/withdraw/pay. The pay callback pulls WBTC from a hardcoded victim address rather than the attacker, letting the attacker withdraw WBTC to their profit receiver while the victim's balance funds the pay step. 85 iterations drain 17 WBTC (~$1.4M).
**Insight:** In lock/callback-based DEX routers, check whether the pay callback's funding source is validated against the caller; a hardcoded or attacker-controlled payer address in the callback lets withdrawals be funded by third parties.
