# Access restrictions on NotionalV1ToNotionalV2.notionalCallback can be bypassed

notionalCallback trusted a caller-supplied sender parameter, letting an attacker frontrun a victim's approval and drain their migration tokens.

- Payout: Audit contest
- Severity: high
- Program: Notional
- Found by: cmichel
- Disclosed: 2021-10-01
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2021-08-notional-findings/issues/71
- Library page: https://bugbountylibrary.com/r/c4%3A2021-08-notional%3AH-05
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control

**Target:** NotionalV1ToNotionalV2.notionalCallback migration contract
**Root cause:** The callback authorized by checking `sender == address(this)`, but `sender` is an attacker-supplied function parameter rather than the real msg.sender, so any caller can satisfy the check.
**Steps:**
1. Victim approves NotionalV1ToNotionalV2 contract and submits migration tx.
2. Attacker frontruns, calling `notionalCallback(sender=address(this), account=victim, ...)` to pass the require.
3. Set `v1RepayAmount=0`, `v1CollateralId=WBTC`, `v2CollateralId=DAI`; `batchOperationWithdraw` pulls victim funds into the contract; deposit matching `v2CollateralBalance` in cheaper DAI.
4. Second tx calls callback with `account=attacker`, `v1DebtCurrencyId=WBTC`, `v1RepayAmount=amount` to deposit stolen funds into attacker's V1 account.
**How found:** Code reading of the callback's authorization: noticed the require compares against a function parameter (`sender`) that the caller controls, rather than msg.sender.
**Exploitation:** Attacker frontruns the victim's migration transaction after approval, using the spoofed sender to withdraw the victim's collateral to the contract and re-deposit it under the attacker's account, netting the difference between withdrawn WBTC and cheaper deposited DAI.
**Insight:** When an auth check compares against a function argument, verify that argument is bound to real caller identity (msg.sender) or a signed/verified value; parameters named like 'sender' are a classic spoofing trap.

