# LiquidationHelper's Swap Mechanism Leads To Stuck Funds That Can Be Stolen

LiquidationHelper accepts arbitrary silo addresses in executeLiquidation, letting anyone drain residual swap profits stuck in the helper via a malicious silo contract.

- Payout: Contest reward
- Severity: medium
- Program: Silo
- Credited: Cosine
- Disclosed: 2025-03-19
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_silo.pdf#3.2.2
- Library page: https://bugbountylibrary.com/r/cantina%3Asilo%3A3.2.2
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic vuln:liquidation

**Target:** LiquidationHelper contract executeLiquidation
**Root cause:** executeLiquidation does not verify the supplied silo is a genuine Silo, and the helper holds residual profit when swap amount is less than liquidation profit; no owner withdrawal path exists.
**Steps:**
1. Attacker observes token balance stuck in a victim's LiquidationHelper.
2. Deploy MaliciousSilo implementing ISilo.flashLoan that calls receiver.onFlashLoan without transferring funds, then transferFrom(msg.sender, this, amount).
3. Deploy MaliciousHookReceiver whose liquidationCall is a no-op.
4. Call `liquidationHelper.executeLiquidation(maliciousSilo, token, 10e18, LiquidationData(maliciousHookReceiver, token, any), new DexSwapInput[](0))`.
5. MaliciousSilo.flashLoan invokes onFlashLoan; helper calls hook (no-op); after callback MaliciousSilo pulls all helper funds.
**How found:** Reviewer noted the helper requires the liquidator to pre-guess profit, leaving residual funds stuck, then observed executeLiquidation lacks silo address validation and that no owner-withdraw exists, enabling theft of those residuals.
**Exploitation:** No capital needed: attacker passes a fake silo and fake hook receiver to executeLiquidation; the fake silo's flashLoan triggers onFlashLoan on the helper, the no-op hook returns zero liquidation, and the fake silo then transfers the helper's entire token balance to itself.
**Insight:** When a helper contract holds residual balances and takes a caller-supplied contract address, check whether that address is validated against a registry/factory — unvalidated callback targets let anyone trigger the helper's own flash-loan flow and siphon its balance.
