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

0 stars 0 forks source link

Lack of `ZetaSent` event availability on Solana could allows an attacker to mint unbacked tokens, causing inflation. #72

Closed c4-submissions closed 11 months ago

c4-submissions commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/2834e3f85b2c7774e97413936018a0814c57d860/repos/protocol-contracts/contracts/zevm/ZetaConnectorZEVM.sol#L67-L86 https://github.com/code-423n4/2023-11-zetachain/blob/2834e3f85b2c7774e97413936018a0814c57d860/repos/protocol-contracts/contracts/evm/ZetaConnector.eth.sol#L52-L70

Vulnerability details

Impact

The ZetaNonEth contract relies on the source chain's ZetaSent event to mint tokens on destination chains. Lack of availability of this event on chains like Solana enables an attacker to mint tokens without actually locking the assets on the source chain. This results in minting unbacked tokens leading to inflation.

Proof of Concept

The cross-chain minting logic relies on the ZetaSent event emitted from the source chain's Connector contract indicating tokens have been locked:

ZetaConnectorZEVM.sol#ZetaSent

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

    constructor(address wzeta_) {
        wzeta = wzeta_;
    }

    /// @dev Receive function to receive ZETA from WETH9.withdraw().
    receive() external payable {
        if (msg.sender != wzeta) revert OnlyWZETA();
    }

The ZetaRelayer/Observer then sees this event on the source chain and mints the tokens on the destination chain by calling the onReceive method:

ZetaConnector.eth.sol#onReceive

    function onReceive(
        bytes calldata zetaTxSenderAddress,
        uint256 sourceChainId,
        address destinationAddress,
        uint256 zetaValue,
        bytes calldata message,
        bytes32 internalSendHash
    ) external override onlyTssAddress {
        bool success = IERC20(zetaToken).transfer(destinationAddress, zetaValue);
        if (!success) revert ZetaTransferError();

        if (message.length > 0) {
            ZetaReceiver(destinationAddress).onZetaMessage(
                ZetaInterfaces.ZetaMessage(zetaTxSenderAddress, sourceChainId, destinationAddress, zetaValue, message)
            );
        }

        emit ZetaReceived(zetaTxSenderAddress, sourceChainId, destinationAddress, zetaValue, message, internalSendHash);
    }

Since the Relayer/Observer only sees the ZetaSent event, if the source chain has data availability issues, the event may not reliably propagate to the Observer.

For example, chains like Solana can temporarily have data availability issues due to network partitions or heavly throttled message queues.

An attacker could leverage this by:

a. Call ZetaConnector.send() to lock tokens b. Call ZetaConnectorNonEth.onReceive() pretending to have seen the blocked ZetaSent event c. ZetaNonEth.mint() is called minting free tokens for the attacker!

At the architecture level, the root cause is that the system relies on the source chain's ZetaSent event to trigger minting on the destination chain.

The expected end-to-end flow is:

  1. User locks tokens on Source Chain via ZetaConnector.send()
  2. ZetaSent event is emitted on Source Chain
  3. Zeta Relayer observes this event and picks it up
  4. Relayer triggers ZetaConnector.onReceive() on Destination Chain
  5. Tokens are minted for User on Destination Chain

However, due to data availability issues on chains like Solana, the actual behavior could be:

  1. User locks tokens on Solana Chain via ZetaConnector.send()
  2. ZetaSent event is emitted but not propagated reliably due to network issues
  3. Relayer never picks up the ZetaSent event
  4. Attacker directly calls ZetaConnector.onReceive() on Destination Chain
  5. Tokens are minted for Attacker without actual lock on Source Chain!

Relying solely on the ZetaSent event to authorize minting. If this event is blocked or unavailable, it breaks the integrity of the asset locking on source chain to minting on destination chain.

This discrepancy in expected vs actual behavior is what enables the asset theft attack.

Tools Used

Vs

Recommended Mitigation Steps

Leverage decentralized message queues like Celestia to ensure cross-chain event delivery

Implement fraud proofs in onReceive() to verify source chain asset locks

Alert integration teams to data availability risks on target chains

Increase mint asset reserves to cover inflation from delayed event flows

Assessed type

Other

DadeKuma commented 11 months ago

Assumes the blockchain is not working

c4-pre-sort commented 11 months ago

DadeKuma marked the issue as insufficient quality report

c4-judge commented 11 months ago

0xean marked the issue as unsatisfactory: Insufficient quality