# Borrower can drain all funds of a sanctioned lender

Wildcat's _blockAccount passes borrower/account args swapped to createEscrow, so a borrower can release a sanctioned lender's escrow and drain their funds.

- Payout: Contest reward
- Severity: high
- Program: Wildcat Protocol
- Credited: YusSecurity
- Disclosed: 2023-12-11
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-10-wildcat-findings/issues/68
- Library page: https://bugbountylibrary.com/r/c4%3A2023-10-wildcat%3AH-06
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** WildcatSanctionsSentinel / WildcatMarketBase escrow creation
**Root cause:** _blockAccount and executeWithdrawal call createEscrow(accountAddress, borrower, asset) but createEscrow expects (borrower, account, asset), so the escrow stores borrower=account and account=borrower. canReleaseEscrow then checks the borrower against the sanctions list instead of the sanctioned lender, returning true.
**Steps:**
1. Lender deposits into borrower's market.
2. Lender gets sanctioned (Chainalysis).
3. Borrower calls `market.nukeFromOrbit(lender)`; _blockAccount creates escrow with swapped args and moves lender's scaledBalance to escrow.
4. Borrower calls `WildcatSanctionsEscrow(escrow).releaseEscrow()`; canReleaseEscrow checks isSanctioned(borrower, account) where account=borrower (unsanctioned), returns true, transfers escrow balance to borrower.
5. Borrower authorizes self as lender via `authorizeLenders(borrower)`, calls `queueWithdrawal(amount)`, waits batch duration, calls `executeWithdrawal(borrower, expiry)` to receive underlying assets.
**How found:** Code review of _blockAccount's createEscrow call against the createEscrow signature and the escrow constructor's tmpEscrowParams destructuring, noticing argument order mismatch and tracing its effect on canReleaseEscrow/isSanctioned.
**Exploitation:** Borrower triggers nukeFromOrbit on a sanctioned lender to move their tokens into a misconfigured escrow, then calls releaseEscrow() which passes the sanctions check because the wrong address is checked, receiving the lender's market tokens; borrower then authorizes self as lender, queues and executes a withdrawal to convert tokens to underlying assets.
**Insight:** When a contract stores constructor params via a sentinel's transient storage (tmpEscrowParams), verify the caller's argument order matches the callee's declared parameter order; a swap silently inverts which address is checked in downstream sanction/authorization logic.
