# Unprotected `executeModule` function allows to steal the tokens

Unprotected executeModule in Tapioca's Usdo lets anyone invoke UsdoMarketReceiver.removeAssetReceiver with a victim's address, withdrawing their Singularity market assets to the attacker.

- Payout: Contest reward
- Severity: high
- Program: Tapioca
- Credited: ComposableSecurity
- Disclosed: 2024-02-14
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2024-02-tapioca-judging/issues/134
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AH-13
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:business-logic

**Target:** Usdo.sol executeModule / UsdoMarketReceiverModule.removeAssetReceiver
**Root cause:** executeModule is externally callable with fully caller-controlled module, data and params, and never checks that the encoded user field matches msg.sender, so any module can be executed on behalf of any user.
**Steps:**
1. Victim userB deposits bUsdo into yieldBox and adds asset to Singularity market, approving magnetar.
2. Attacker userA builds MarketRemoveAssetMsg with user=userB, removeAssetFromSGL=true, removeAmount=tokenAmountSD, assetWithdrawData.withdraw=true, dstEid=0, and sendParam.to=attacker address.
3. Attacker calls `bUsdo.executeModule(IUsdo.Module.UsdoMarketReceiver, abi.encodeWithSelector(UsdoMarketReceiverModule.removeAssetReceiver.selector, marketMsg_), false)`.
4. removeAssetReceiver forwards to Magnetar exitPositionAndRemoveCollateral, which removes userB's asset from Singularity and calls _withdrawToChain; dstEid==0 routes to _withdrawHere transferring to sendParam.to (attacker).
5. Assert userB balance is 0 and attacker received tokenAmount_.
**How found:** Code reading of executeModule revealed all parameters are caller-controlled with no sender validation; researcher then traced the UsdoMarketReceiver module into Magnetar's exitPositionAndRemoveCollateral and _withdrawToChain to find a path where dstEid=0 sends withdrawn assets to an arbitrary recipient.
**Exploitation:** Single unauthenticated call to executeModule with a crafted MarketRemoveAssetMsg; no capital needed since victim's existing approvals to magnetar are reused to pull and withdraw their market position directly to the attacker's address.
**Insight:** When a dispatcher function takes a module enum plus arbitrary calldata, trace each module's downstream calls for a 'user' or 'to' field that is trusted from calldata rather than msg.sender; a withdraw path with a zero-destination branch that pays out locally is a prime exfil sink.
