code-423n4 / 2023-11-zetachain-findings

0 stars 0 forks source link

Lack of message ordering may lead to failed transactions #395

Open c4-bot-8 opened 11 months ago

c4-bot-8 commented 11 months ago

Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L31-L45

Vulnerability details

Impact

Cross-chain messages might be processed out-of-order, leading to failed transactions and dApps that are not working as expected.

Proof of Concept

Cross-chain messages can be sent between external chains by using the ZetaConnectorEth.send function. This function emits the ZetaSent event that is subsequently picked up by the ZetaChain observers and voted upon.

31: function send(ZetaInterfaces.SendInput calldata input) external override whenNotPaused {
32:     bool success = IERC20(zetaToken).transferFrom(msg.sender, address(this), input.zetaValueAndGas);
33:     if (!success) revert ZetaTransferError();
34:
35:     emit ZetaSent(
36:         tx.origin,
37:         msg.sender,
38:         input.destinationChainId,
39:         input.destinationAddress,
40:         input.zetaValueAndGas,
41:         input.destinationGasLimit,
42:         input.message,
43:         input.zetaParams
44:     );
45: }

Such a cross-chain transaction is considered valid (finalized), once a quorum of observers has voted on the transaction and verified the legitimacy of the transaction. This process naturally takes a certain amount of time, as the observers wait for the including block (on the external chain) to be confirmed by a reasonable amount of subsequent blocks. Additionally, observers are not 100% synchronized, meaning that some observers might have seen the including block earlier than others.

As a result, cross-chain transactions, or inbound cctxs in general, are not guaranteed to be finalized in the order they were sent from the external chain.

This can lead to a situation where a contract on an external chain that sends two consecutive cross-chain messages and expects the second message to be processed after the first message, is not guaranteed to be processed in the correct order.

For example, consider the simplified example of a staking dApp which sends multiple consecutive cross-chain messages in the same block or transaction (please ignore the reasoning why a user would stake and immediately unstakes, this should just demonstrate the dependency on the order of the messages):

  1. stake
  2. unstake

Those two messages have to be in order. If message 2 (unstake) is executed before message 1 (stake), the transaction reverts on the destination chain as there is no stake to unstake.

Even if the dApp would incorporate a nonce in the cross-chain message, the receiving contract on the destination chain would reject the out-of-order message as the nonce is not the next expected nonce and revert the transaction.

Tools Used

Manual review

Recommended mitigation steps

Consider adding the option to enforce message ordering per sender and source chain, similar to LayerZero's message ordering feature.

Assessed type

Other

c4-pre-sort commented 11 months ago

DadeKuma marked the issue as sufficient quality report

c4-pre-sort commented 11 months ago

DadeKuma marked the issue as primary issue

c4-sponsor commented 10 months ago

lumtis (sponsor) confirmed

c4-judge commented 10 months ago

0xean marked the issue as satisfactory

c4-judge commented 10 months ago

0xean marked the issue as selected for report