# isCoolerCallback can be bypassed

Lender bypasses isCoolerCallback check via a stub contract or loan ownership transfer, then omits onRepay/onRoll so borrower cannot repay and loan defaults, letting lender seize collateral.

- Payout: Contest reward
- Severity: high
- Program: Cooler
- Credited: 0xbepresent
- Disclosed: 2023-08-17
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-08-cooler-judging/issues/187
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-08-cooler%3AH-4
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** Cooler lending contract clearRequest/transferOwnership
**Root cause:** clearRequest only checks CoolerCallback(msg.sender).isCoolerCallback() returns true, not that the lender actually implements onRepay/onRoll/onDefault; transferOwnership also moves a callback=true loan to any address without re-validating.
**Steps:**
1. Deploy contract with `isCoolerCallback() returns true` and only `onDefault()` implemented (onRepay/onRoll revert or absent).
2. Call `Cooler.clearRequest(reqID_, true, true)` from that contract to become lender with callback=true.
3. Borrower calls `repayLoan()` or `rollLoan()`; call reverts in onRepay/onRoll.
4. Loan expires; attacker calls `claimDefault()` to take collateral.
5. Alternative: original lender calls `approveTransfer(attacker, loanID)` then attacker `transferOwnership(loanID)`; callback flag stays true.
**How found:** Code reading of clearRequest's isCoolerCallback check and noticing the interface check only verifies a boolean-returning function, plus reviewing transferOwnership which preserves the callback flag without re-validation.
**Exploitation:** Lender registers as callback-enabled via stub contract or receives loan ownership via approveTransfer/transferOwnership, blocking repay/roll paths so the loan defaults and collateral is claimed by the lender.
**Insight:** Interface-existence checks like `X(msg.sender).isX()` only prove a function returns true, not that the full abstract is implemented; also audit ownership-transfer paths that carry over capability flags without re-validating the new owner.
