# TOFT removeCollateral can be used to steal all the balance

Unvalidated marketHelper and withdrawLzFeeAmount in TOFT removeCollateral let attacker pass malicious contract, draining native-token balance via withdrawToChain as the TOFT itself.

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

**Target:** BaseTOFT removeCollateral / remove message pathway (LayerZero cross-chain)
**Root cause:** The remove() handler trusts attacker-supplied removeParams.marketHelper and withdrawParams.withdrawLzFeeAmount without validation, and calls withdrawToChain with msg.sender equal to the TOFT contract, which holds the native balance.
**Steps:**
1. Deploy malicious IMarket contract implementing removeCollateral (no-op), yieldBox() and collateralId() returning arbitrary values.
2. Deploy malicious marketHelper contract implementing withdrawToChain that forwards/uses the value sent.
3. Call removeCollateral on any chain targeting the chain holding the native balance, encoding removeParams.market=malicious market, removeParams.marketHelper=malicious helper, withdrawParams.withdraw=true, withdrawParams.withdrawLzFeeAmount=TOFT native balance.
4. Relayer delivers message; lzReceive -> _blockingLzReceive -> delegatecall into BaseTOFTMarketModule preserves msg.sender as the TOFT.
5. remove() calls approve, then IMarket(market).removeCollateral (no-op), then IMagnetar(marketHelper).withdrawToChain{value: withdrawLzFeeAmount}(...) with msg.sender = TOFT.
6. Malicious helper receives the TOFT's full native balance and forwards it to attacker.
**How found:** Code review of the remove() message handler: noticed marketHelper and withdrawLzFeeAmount are not validated on the sending side, and traced the msg.sender chain through lzReceive -> _blockingLzReceive -> delegatecall to confirm the TOFT itself is the caller holding the balance.
**Exploitation:** Attacker crafts a LayerZero message invoking removeCollateral with a malicious market and marketHelper and withdrawLzFeeAmount set to the TOFT's native balance. On the destination chain the delegatecall preserves the TOFT as msg.sender, so withdrawToChain sends the entire native balance to the attacker-controlled helper.
**Insight:** In cross-chain message handlers using delegatecall, trace who msg.sender becomes at the sink: here it is the token contract itself, so any unvalidated callee address plus unvalidated value parameter becomes a drain of the contract's own balance.
