# Fake ZetaReceived events cause the outbound cctx to remain pending resulting in a blocked outbound EVM transaction queue

ZetaChain observer parses ZetaReceived events without verifying the emitter, so a receiver contract can emit a fake event with a bogus internalSendHash, leaving the cctx PendingOutbound and blocking the outbound EVM queue.

- Payout: Contest reward
- Severity: high
- Program: ZetaChain
- Credited: berndartmueller ([1](https://github.com/code-423n4/2023-11-zetachain-findings/issues/418)
- Disclosed: 2024-09-06
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-11-zetachain-findings/issues/418
- Library page: https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AH-03
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:evm technique:source-review vuln:access-control vuln:dos vuln:misconfiguration

**Target:** ZetaChain zetaclient EVM observer (evm_client.go IsSendOutTxProcessed)
**Root cause:** ParseZetaReceived only checks the event signature, not the emitting contract address, so any contract called during onReceive/onRevert can emit a spoofed ZetaReceived log that the observer accepts.
**Steps:**
1. Deploy attacker contract implementing ZetaReceiver that emits a ZetaReceived event with a non-existent internalSendHash in onZetaMessage.
2. Call connector.send() with destinationChainId=1337 (same local chain) and the attacker as destinationAddress.
3. Observer's IsSendOutTxProcessed parses the fake ZetaReceived log without checking emitter, sends MsgVoteOnObservedOutboundTx with the bogus hash, and returns early, ignoring the legitimate ZetaReverted event.
4. Vote fails because internalSendHash does not match any cctx, so cctx stays PendingOutbound.
5. Stuck cctx becomes cctxList[0]; once a later cctx nonce exceeds it by MaxLookaheadNonce (120), the break in zetacore_observer.go halts all further outbound sends.
**How found:** Code review of evm_client.go IsSendOutTxProcessed: noticed ParseZetaReceived only unpacks the log by signature and never checks log.Address against the connector, and that the loop returns on the first match, so a receiver-emitted fake event shadows the real one.
**Exploitation:** Attacker sends a cross-chain message to a self-controlled receiver contract that emits a fake ZetaReceived event with an internalSendHash pointing to a non-existent cctx. The observer accepts the spoofed log, votes with the wrong hash, the vote fails, and the cctx is stuck pending, eventually throttling the entire outbound queue via the MaxLookaheadNonce check.
**Escalation:** A single stuck cctx at the head of cctxList blocks all subsequent outbound transactions once the nonce gap exceeds MaxLookaheadNonce, causing protocol-wide outbound DoS.
**Insight:** When an off-chain observer trusts EVM logs, check whether it validates log.Address (emitter) and not just the event topic; any contract invoked mid-transaction can emit arbitrary events, and early-return-on-first-match lets a fake shadow the legitimate one.
