# Zebra has block suppression via NU5 same-header body poisoning of sent-hash cache

Zebra caches block hashes before validation; a peer's poisoned same-header body leaves a stale entry, so the valid block is rejected as duplicate and the node stalls.

- Payout: Research
- Severity: high
- Program: Zebra
- Found by: ipwning, x15-eth
- Disclosed: 2026-07-02
- CVE: CVE-2026-52736
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-4m69-67m6-prqp
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-4m69-67m6-prqp
- Tags: area:web3 domain:web impact:dos kind:research platform:ghsa sev:high tech:rust technique:race-window technique:source-review vuln:business-logic vuln:dos

**Target:** Zcash full node P2P block sync (zebra-state write channel)
**Root cause:** Block hash is inserted into `non_finalized_block_write_sent_hashes` before contextual validation and never removed when the write fails, so a rejected body permanently marks the hash as already-sent.
**Steps:**
1. Observe a new block header from any peer.
2. Construct a poisoned body by flipping a byte of the coinbase scriptSig extra-data section; header hash unchanged (ZIP-244 txid_v5 doesn't bind scriptSig).
3. Advertise the block hash via `inv` to the target node.
4. Target requests via `getdata`; serve the poisoned body.
5. Zebra adds hash to `non_finalized_block_write_sent_hashes` before validation.
6. Write task rejects body at `block_commitment_is_valid_for_chain_history` (auth_data_root mismatch); hash stays cached.
7. When the valid canonical block arrives, `queue_and_commit_to_non_finalized_state` returns `KnownBlock::WriteChannel` duplicate and rejects it; node stuck at height N-1.
**How found:** ZIP-244 malleability analysis: txid_v5 doesn't commit to transparent scriptSig, so merkle_root over txids plus header-only block hash allows two bodies with identical header hash; cross-referenced zcashd and confirmed with E2E reproduction and control experiment.
**Exploitation:** Attacker races honest peers: low-latency connection to target, observes new headers, serves poisoned same-header body first so the hash is cached pre-validation; repeated per block keeps target permanently behind tip.
**Escalation:** Sustained repetition for each new block keeps the node permanently diverged; secondary variant via chain pruning (MAX_NON_FINALIZED_CHAIN_FORKS) leaves hashes of pruned forks cached, locking out children.
**Insight:** When a dedup/cache key is a hash that doesn't commit to the full body (Zcash txid_v5 vs auth_digest), an attacker can poison the cache with a rejected variant and block the valid one; audit insert-before-validate paths for cleanup on failure.

