# The implementation of pullTokensWithPermit poses a risk, allowing malicious actors to steal tokens

VaultRouter's pullTokensWithPermit accepts any caller's permit signature, letting an attacker frontrun, pull victim tokens into the router, then sweepTokens them out.

- Payout: Audit contest
- Severity: high
- Program: BakerFi
- Found by: shaflow2
- Disclosed: 2025-02-26
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2024-12-bakerfi-invitational#h-05
- Library page: https://bugbountylibrary.com/r/c4%3A2024-12-bakerfi-invitational%3AH-05
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:race-window technique:source-review vuln:access-control vuln:signature-replay

**Target:** VaultRouter contract / UsePermitTransfers hook (Solidity)
**Root cause:** pullTokensWithPermit calls token.permit(owner, address(this), ...) and safeTransferFrom without binding the caller to the owner, so anyone holding a valid permit signature can pull the owner's tokens into the router.
**Steps:**
1. Victim submits tx calling pullTokensWithPermit(token, 1000e18, victim, deadline, v, r, s) to deposit into a vault.
2. Attacker observes the tx in mempool and extracts the permit signature.
3. Attacker calls pullTokensWithPermit with the same args, executing permit(victim, router, amount) and safeTransferFrom(victim, router, amount).
4. Attacker calls sweepTokens to transfer the pulled tokens to their own address.
5. Victim's original tx reverts because the permit nonce is consumed; victim loses the tokens.
**How found:** Code review of the permit-based transfer hook: noticed the router never checks that msg.sender relates to owner, and that a public sweepTokens exists, making the permit signature reusable by anyone.
**Exploitation:** Mempool frontrun: replay the victim's permit signature through the permissionless router, then drain via sweepTokens (or call pushTokenFrom directly after frontrunning the permit).
**Insight:** When a contract wraps EIP-2612 permit + transferFrom, check whether the caller is bound to the owner; if a public sweep/drain function exists, any permit signature in the mempool is a theft primitive.

