Open sujithsomraaj opened 5 months ago
I fear that is not easily possible as depending from which chain we interact with stargate the address may be different.
The main question is, if that is really needed or if stargate itself already validates that. As far as I understood so far it should not matter who the _from
address is, because by validating that the calls come from the stargate executor (onlyEndpointV2
) we make sure that stargate delivered those tokens to our contract before.
Hey @maxklenk, yes, the _from
address validation is mandatory. If you take a deeper look into the layerzero endpoint, anyone could compose a message to be delivered to the Receiver contract.
So anyone could be made to call the lzCompose in the receiver contract from the endpoint. So trusting the endpoint alone is not a valid protection
I see, thanks for checking. We have to improve that @0xDEnYO
Yes, that is correct. Very good that you found this one @sujithsomraaj
Since the pool/_from address will differ depending on the token being bridged I would suggest the following mitigation approach:
expStargatePoolAddress
and then compare this with _from
parameter which leaves it to our backend to determine the correct pool/token and pass it into the payload.What do you think? @maxklenk @sujithsomraaj
As discussed in the internal call today this is not sufficient, but checking that the _from
address is a stargate pool by verifying that on the tokenMessenger contract will solve the problem.
function lzCompose(
address _from,
bytes32, // _guid (not used)
bytes calldata _message,
address, // _executor (not used)
bytes calldata // _extraData (not used)
) external payable onlyEndpointV2 {
// verify that _from address is actually a Stargate pool by checking if Stargate's
// TokenMessaging contract has an assetId registered for this address
if (tokenMessaging.assetIds(_from) == 0) revert UnAuthorized();
// get the address of the token that was received from Stargate bridge
address bridgedAssetId = IPool(_from).token();
// decode payload
(
bytes32 transactionId,
LibSwap.SwapData[] memory swapData,
address receiver
) = abi.decode(
OFTComposeMsgCodec.composeMsg(_message),
(bytes32, LibSwap.SwapData[], address)
);
// execute swap(s)
_swapAndCompleteBridgeTokens(
transactionId,
swapData,
bridgedAssetId,
payable(receiver),
OFTComposeMsgCodec.amountLD(_message)
);
}
This is the updated Receiver code that also validates the _from
parameter.
Please could you comment. Can we close this issue with this solution? @sujithsomraaj @maxklenk @ezynda3
Looks good to me. Nice solution.
LGTM
New solution accepted - issue closed.
Context: ReceiverStargateV2.sol#L84
Description: The function
lzCompose
is called byLayerzeroEndpoint
to deliver a composed message from OApp (stargate in our case); however, the current implementation oflzCompose
fails to validate if the_from
address is trusted stargate OApp.As a result, any contract that implements the
IPool
interface could pose as a valid stargate pool and steal tokens from theReceiverStargateV2
contract.Recommendation: Implement checks to ensure the
_from
address is a valid Stargate OApp address on the relevant chain. For more information, please refer to Layerzero docs and stargate docs.LI.FI:
Researcher: