# There are no Illuminate PT transfers from the owner in ERC5095's withdraw and redeem before maturity

Illuminate ERC5095 withdraw/redeem pre-maturity sell PTs from the contract's own balance instead of the owner's, letting anyone drain held PTs and breaking normal withdrawals.

- Payout: Contest reward
- Severity: high
- Program: Illuminate
- Credited: cryptphi
- Disclosed: 2022-10-15
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2022-10-illuminate-judging/issues/195
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-10-illuminate%3AH-7
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** ERC5095 principal token contract (withdraw/redeem) via Marketplace.sellPrincipalToken
**Root cause:** Pre-maturity withdraw()/redeem() call Marketplace.sellPrincipalToken which pulls PTs from msg.sender (the ERC5095 contract) via Safe.transferFrom, but never transfer shares from owner `o` nor update owner records; the `o == msg.sender` check is meaningless.
**Steps:**
1. Call `withdraw(a, r, o)` or `redeem(s, r, o)` on ERC5095 before maturity with any `o`
2. Function computes shares and calls `IMarketPlace(marketplace).sellPrincipalToken(underlying, maturity, shares, ...)`
3. Marketplace does `Safe.transferFrom(pool.fyToken(), msg.sender=ERC5095, pool, a)` pulling PTs from the contract's own balance
4. Underlying proceeds sent to `r`; owner `o` balance/allowance untouched
5. Repeat until contract PT balance is empty; legitimate owner withdrawals then revert for lack of balance
**How found:** Code reading of withdraw/redeem pre-maturity branch: noticed sellPrincipalToken pulls from msg.sender (the contract) and that owner `o` is never debited or transferred from, making the `o == msg.sender` branch a no-op check.
**Exploitation:** Attacker calls withdraw/redeem pre-maturity with arbitrary owner address; contract sells its own PT holdings on the YieldSpace AMM and forwards underlying to attacker-chosen receiver, draining any PTs held by the ERC5095 contract at no cost.
**Insight:** In wrapper/token contracts that delegate selling to a marketplace, verify the shares being sold are actually pulled from the owner (transferFrom owner->this) before the external sell call; a self-referential `o == msg.sender` shortcut that skips allowance is a red flag for missing owner debit.
