# Outbound Confirmation Tracker Race Condition Leads to a Double Spend

Race condition in ZetaChain's outbound tx tracker lets an attacker inject a fake reverted tx with the TSS nonce, causing double spend or DoS of CCTXs.

- Payout: Contest reward
- Severity: high
- Program: ZetaChain
- Credited: ChristiansWhoHack
- Disclosed: 2024-09-06
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-11-zetachain-findings/issues/338
- Library page: https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AH-11
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:evm technique:incomplete-fix technique:race-window vuln:business-logic vuln:race-condition

**Target:** zetaclient outbound confirmation tracker (evm_client.go)
**Root cause:** observeTxOut stores the first tx hash added to OutTxTracker into outTXConfirmationTransaction/Receipts without validating the sender is the TSS address or that events match; only the nonce is checked, which any key can match.
**Steps:**
1. Query next CCTX nonce: `zetacored query crosschain list-chain-nonces -o json | jq '.ChainNonces[] | select(.chain_id=="1337").nonce'`
2. Deploy ForcedRevert contract and call `revertMe()` repeatedly until a reverted tx hash has the same nonce as the TSS address
3. Deploy DoubleSpend contract implementing ZetaReceiver; call `TriggerBug()` to send a CCTX to chain 1337
4. Wait until CCTX is `PendingOutbound` (`zetacored query crosschain list-pending-cctx 1337`)
5. Immediately call `zetacored tx crosschain add-to-out-tx-tracker 1337 <TSS_NONCE> <FAKE_HASH>` before zetaclient adds the real tx
6. observeTxOut picks the fake reverted tx first; PostReceiveConfirmation votes it as reverted
7. Revert flow returns funds on source chain despite original tx succeeding -> double spend; or CCTX stuck -> DoS
**How found:** Followed up on Zellic finding 3.2 (RemoveFromOutTxTracker access control) after it was patched, hypothesizing the same removal effect could be replicated by racing a fake tx into the tracker; noticed only the first tracker entry is used and only nonce is validated, not sender/events.
**Exploitation:** Attacker creates a CCTX, then races to insert a self-sent reverted tx (same nonce as TSS) into OutTxTracker before zetaclient registers the real outbound. The fake tx is processed first, voted as reverted, and the revert path refunds the attacker while the original transfer already executed. Timing window is tight (~5s) for double spend; DoS is trivial.
**Insight:** When a fix removes an access-control primitive, look for a race that reproduces the same state change; here the tracker trusts the first hash by nonce alone, so any key can front-run with a reverted tx matching the TSS nonce.
