Assessment reports>ZetaChain>Critical findings>Any ,ZetaSent, events are processed regardless of what contract emits them
Category: Coding Mistakes

Any ZetaSent events are processed regardless of what contract emits them

Critical Severity
Critical Impact
High Likelihood

Description

The main method through which funds are intended to be bridged over from the zEVM to another chain is by calling the send() function in the zEVM's ZetaConnectorZEVM contract. This function emits a ZetaSent event, which is intended to be processed by the crosschain module's PostTxProcessing() hook.

It is crucial that this function checks to ensure that any ZetaSent event it picks up originated from the ZetaConnectorZEVM contract. Otherwise, any malicious attacker can deploy their own contract on the zEVM and emit arbitrary ZetaSent events to send arbitrary amounts of ZETA without actually holding any ZETA.

Impact

Inside the PostTxProcessing() hook, we see the following:

func (k Keeper) PostTxProcessing(
	ctx sdk.Context,
	msg core.Message,
	receipt *ethtypes.Receipt,
) error {
	target := receipt.ContractAddress
	if msg.To() != nil {
		target = *msg.To()
	}
	for _, log := range receipt.Logs {
		eZRC20, err := ParseZRC20WithdrawalEvent(*log)
		if err == nil {
			if err := k.ProcessZRC20WithdrawalEvent(ctx, eZRC20, target, ""); err != nil {
				return err
			}
		}
		eZeta, err := ParseZetaSentEvent(*log)
		if err == nil {
			if err := k.ProcessZetaSentEvent(ctx, eZeta, target, ""); err != nil {
				return err
			}
		}
	}
	return nil
}

The receipt parameter of this function contains information about transactions that occur on the zEVM. This function iterates through all logs (i.e., emitted events) in each receipt and attempts to parse the events as Withdrawal or ZetaSent events.

However, there is no check to ensure that these events originate from the ZetaConnectorZEVM contract. This allows a malicious attacker to deploy their own contract on the zEVM, which would allow them to emit arbitrary ZetaSent events, and thus gain access to ZETA tokens that they otherwise should not have access to.

The prototype of the ZetaSent event is as follows:

event ZetaSent(
    address sourceTxOriginAddress,
    address indexed zetaTxSenderAddress,
    uint256 indexed destinationChainId,
    bytes destinationAddress,
    uint256 zetaValueAndGas,
    uint256 destinationGasLimit,
    bytes message,
    bytes zetaParams
);

It is important to note that Withdrawal events are not affected by this bug. The ProcessZRC20WithdrawalEvent() function checks and ensures that the event was emitted from a whitelisted ZRC20 token contract address.

Recommendations

Add a check to ensure that ZetaSent events are only processed if they are emitted from the ZetaConnectorZEVM contract.

Remediation

This issue has been acknowledged by ZetaChain, and a fix was implemented in commit 8a988ae9.

Zellic © 2024Back to top ↑