The _call1InchSwap() function passes raw user-supplied calldata directly to the 1inch router with zero validation of what the calldata actually does. The dstReceiver field inside the 1inch swap descriptor — which controls where swapped tokens go — is never decoded or checked.
The calldata is forwarded verbatim. Nothing verifies that dstReceiver == address(this), that srcToken matches the expected borrow token, or that dstToken matches the expected collateral token.
Impact
An owner (or compromised key) can supply 1inch calldata routing swap output to an external address. The contract's borrow token consumption check (afterSwapBorrowTokenBalance == prevBorrowTokenBalance) only verifies that borrow tokens LEFT the contract — it doesn't verify that collateral tokens ARRIVED. Funds can be silently redirected.
Call createLeveragedPosition() with _oneInchSwapData encoding a swap where dstReceiver = attacker_wallet
Contract borrows USDC, approves 1inch, swap executes successfully
Swapped WETH goes to attacker wallet, not address(this)
Borrow token balance check passes (USDC was consumed ✓)
Flash loan repayment fails OR pre-existing balance covers it depending on sizing
More realistic attack — value extraction via inflated borrow:
The attacker doesn't need to steal in one shot. They can use the misconfigured swap to:
Borrow MORE than needed (overcollateralized borrow with user's locked collateral)
Swap borrowed tokens to a shitcoin with attacker-controlled liquidity (sandwiching their own swap)
Pocket the difference
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.