# Unbound callback initiator in DeFi

Callback trusts caller-supplied payer or initiator instead of binding it to the real caller

_Area: Smart contract access control and control flow. Seen in 3 disclosed reports; latest 2026-05-05. Updated 2026-11-01._

## How it works
A contract hands control to a pool/router/lender through a callback, then trusts a field in the callback data (a payer address, an initiator, a boolean capability flag) to decide whose funds move or whose position is touched. The assumption that breaks: that the callback can only be reached by the party who started the operation. In practice anyone can trigger the callback, or set the receiver/initiator/payer to the victim, so the contract acts on the victim's balance or position as if the victim had asked [E1](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md)[E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md)[E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md). A weaker form: the code checks that a caller *looks* capable (returns true from an interface method) rather than that it actually implements the required behaviour [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md).

## Where it shows up
- Callback handlers that decode a `payer`, `initiator`, `receiver`, or `seller` from calldata and use it without comparing to the original caller [E1](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md)[E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md)[E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md).
- Flash-loan `executeOperation`-style hooks that check `msg.sender == pool` but not `_initiator == address(this)` [E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md).
- Interface-existence checks like `X(msg.sender).isX()` gating a privileged path [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md).
- Ownership-transfer or upgrade paths that carry a capability flag (`callback = true`) to a new address without re-validating it [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md).
- Lock/forward/withdraw/pay router flows where the pay step pulls from a fixed or attacker-chosen address [E1](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md).

## How to review
Find every callback entry point and ask: what binds this call to the party who initiated it? The invariant: the funding source and the affected position must equal the address that called the initiating function, cached before the external call, not read from callback calldata. Check that `msg.sender` in the callback is the trusted pool *and* that the initiator/receiver/payer equals the contract or the original caller [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md)[E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md). For capability checks, confirm the full interface is implemented, not just a boolean [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md). Trace ownership transfers and re-check flags after them [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md).

## How finders spotted it
- Read `clearRequest`'s `isCoolerCallback` check, saw it only proves a function returns true, then checked `transferOwnership` which keeps the flag without re-validation [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md).
- Reviewed the sell flow, noticed the callback deposits from `decodeData.payer` and that nothing ties payer to the seller (msg.sender is the pool) [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md).
- Realised Aave flash loans can name any receiver, and repayment is pulled from the receiver, so a missing `_initiator` check lets anyone trigger the hook [E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md).

## Variants seen
- Hardcoded/attacker-controlled payer in a router pay callback, repeated across many lock iterations [E1](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md).
- Boolean interface check bypassed via stub contract or ownership transfer [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md).
- Attacker-controlled `payer` in swap callback calldata draining approved tokens [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md).
- Missing flash-loan initiator check enabling position close and theft [E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md).

## Impact and severity
Direct fund theft: ~$1.4M drained via repeated callback withdrawals [E1](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md); victims' approved tokens sold with proceeds kept [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md); positions closed and funds stolen [E4](https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md); borrower forced into default so lender seizes collateral [E2](https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md). Impact scales with standing approvals and repeatability across users [E3](https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md), and with iteration count in lock loops [E1](https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md).

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20260505-ekubo.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2023-06-dodo%3AH-3.md
- E4: https://bugbountylibrary.com/r/sherlock%3A2023-05-dodo%3AH-1.md
