CVE-2026-52736: Zebra state Zebra Has Block Suppression Vulnerability

HIGH
Advisory Identifier: GHSA-4m69-67m6-prqp
CWE-459
Published
Advisory Summary

CVE-2026-52736 is a HIGH zebra has block suppression vulnerability (CWE-459: Incomplete Cleanup) affecting Zebra state libraries in the crates.io ecosystem. The issue involves zebra has block suppression via nu5 same-header body poisoning of sent-hash cache. Remediation is available by upgrading affected packages to version 7.0.0, 4.5.0.

CVE-2026-52736 at a Glance

CVE Identifier CVE-2026-52736
GHSA Identifier GHSA-4m69-67m6-prqp
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-459: Incomplete Cleanup
Affected Product Zebra state
Package Ecosystem crates.io
Published Date
Last Updated

How to Fix CVE-2026-52736

To remediate this vulnerability, upgrade all affected Zebra state dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

zebra-state fixed: 7.0.0
Upgrade zebra-state to 7.0.0
zebrad fixed: 4.5.0
Upgrade zebrad to 4.5.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for zebra-state

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-52736, inspect your project dependencies for Zebra state:

inspect dependencies for zebra-state

Analyze the terminal output to locate any version of Zebra state in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 7.0.0), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-459: Incomplete Cleanup, where the product does not properly "clean up" and remove temporary or supporting resources after they have been used. In Zebra state, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Description ### Am I affected You are affected if: 1. You run any version of `zebrad` up to and including `v4.4.1`. 2. Your node accepts inbound P2P connections (`network.listen_addr` is set, which is the default). 3. Your node processes blocks past the checkpoint height (non-finalized state is active). All default configurations are affected. ### Summary Zebra records a block hash in `non_finalized_block_write_sent_hashes` when the block is sent to the write task, before contextual validation completes. If validation fails, the hash is not removed. A remote unauthenticated peer can deliver a poisoned block body that shares a header hash with a later valid canonical block. The poisoned body is rejected, but the hash remains cached. When the valid canonical block arrives, Zebra treats it as a duplicate and rejects it. The node cannot advance past that height until restart or a reorg event. ### Details ZIP-244 defines `txid_v5` without binding transparent input `scriptSig`, which lives in `auth_digest` and is committed to by `hashBlockCommitments` in the block header. Because `merkle_root` is computed over txids (not auth digests), and the block hash is computed over the header, an attacker can construct two blocks with identical header hashes but different transaction bodies by mutating the coinbase scriptSig. The attack flow over P2P: 1. Attacker observes a new block header (from any peer). 2. Attacker constructs a poisoned body by flipping a byte of the coinbase scriptSig extra-data section. The block hash is unchanged. 3. Attacker advertises the block hash via `inv` to the target node. 4. Target requests the block via `getdata`; attacker serves the poisoned body. 5. Zebra adds the hash to `non_finalized_block_write_sent_hashes` before validation. 6. The write task rejects the body at `block_commitment_is_valid_for_chain_history` (auth_data_root mismatch). 7. The hash is not removed from `non_finalized_block_write_sent_hashes`. 8. When the valid canonical block arrives (from honest peers or RPC), `queue_and_commit_to_non_finalized_state` sees the hash in the cache and returns `KnownBlock::WriteChannel` duplicate. 9. The node is stuck at height N-1. A secondary variant exists where chain pruning (via `MAX_NON_FINALIZED_CHAIN_FORKS`) removes a chain from `chain_set` but leaves its block hashes in `non_finalized_block_write_sent_hashes`, producing the same lockout for children of the pruned fork. ### Patches Patched in Zebra 4.4.2. The fix removes stale entries from `non_finalized_block_write_sent_hashes` on every failed non-finalized write path. ### Workarounds There is no complete configuration-level workaround. Reducing the node's inbound peer count (`network.peerset_initial_target_size`) narrows the attack surface but does not eliminate it. Restarting the node clears the in-memory cache and allows the valid block to be re-fetched. ### Impact A remote unauthenticated P2P peer can permanently stall a targeted Zebra node at a specific block height. The node diverges from the network tip; downstream consumers (lightwalletd, wallets, explorers, mining infrastructure) relying on the node see a stalled chain. The attack requires winning a propagation race: delivering the poisoned block body before honest peers deliver the canonical block. A well-positioned attacker (low-latency connection to the target, observation of new blocks from other peers) can reliably win this race. In sustained form, the attacker repeats for each new block, keeping the target permanently behind. Recovery requires restarting the node (which clears the in-memory sent-hash cache) or waiting for a reorg at the affected height (rare on the canonical chain). ### Credit Reported independently by `@ipwning` (primary, with ZIP-244 malleability analysis and zcashd cross-reference) and `@x15-eth` (first reporter, with E2E reproduction and control experiment).

CVE-2026-52736 Affected Zebra state Versions

2 component(s)
Fixed Versions:
7.0.04.5.0
Product / Package Ecosystem Affected Versions Fixed Version
zebra-state
crates.io
< 7.0.0
7.0.0
zebrad
crates.io
< 4.5.0
4.5.0

References

Frequently Asked Questions

What is CVE-2026-52736?
CVE-2026-52736 is a security vulnerability in Zebra state involving Zebra has block suppression via NU5 same-header body poisoning of sent-hash cache under CWE-459: Incomplete Cleanup.
Which Zebra state versions are affected?
Affected versions include releases prior to 7.0.0, 4.5.0 across affected packages: zebra-state, zebrad.
What version fixes CVE-2026-52736?
Upgrading to 7.0.0, 4.5.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is zebra-state affected?
Yes, zebra-state is affected in releases prior to 7.0.0. Upgrade your dependency to 7.0.0 or later.
Is zebrad affected?
Yes, zebrad is affected in releases prior to 4.5.0. Upgrade your dependency to 4.5.0 or later.
What CWE is associated with CVE-2026-52736?
CVE-2026-52736 is classified under CWE-459: Incomplete Cleanup (The product does not properly "clean up" and remove temporary or supporting resources after they have been used.).
How can I check crates.io dependencies for CVE-2026-52736?
Run the dependency tree command: 'inspect dependencies for zebra-state'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-4m69-67m6-prqp",
  "modified" : "2026-07-02T19:43:08Z",
  "published" : "2026-07-02T19:43:08Z",
  "aliases" : [ "CVE-2026-52736" ],
  "summary" : "Zebra has block suppression via NU5 same-header body poisoning of sent-hash cache",
  "details" : "## Description\n\n### Am I affected\n\nYou are affected if:\n\n1. You run any version of `zebrad` up to and including `v4.4.1`.\n2. Your node accepts inbound P2P connections (`network.listen_addr` is set, which is the default).\n3. Your node processes blocks past the checkpoint height (non-finalized state is active).\n\nAll default configurations are affected.\n\n### Summary\n\nZebra records a block hash in `non_finalized_block_write_sent_hashes` when the block is sent to the write task, before contextual validation completes. If validation fails, the hash is not removed. A remote unauthenticated peer can deliver a poisoned block body that shares a header hash with a later valid canonical block. The poisoned body is rejected, but the hash remains cached. When the valid canonical block arrives, Zebra treats it as a duplicate and rejects it. The node cannot advance past that height until restart or a reorg event.\n\n### Details\n\nZIP-244 defines `txid_v5` without binding transparent input `scriptSig`, which lives in `auth_digest` and is committed to by `hashBlockCommitments` in the block header. Because `merkle_root` is computed over txids (not auth digests), and the block hash is computed over the header, an attacker can construct two blocks with identical header hashes but different transaction bodies by mutating the coinbase scriptSig.\n\nThe attack flow over P2P:\n\n1. Attacker observes a new block header (from any peer).\n2. Attacker constructs a poisoned body by flipping a byte of the coinbase scriptSig extra-data section. The block hash is unchanged.\n3. Attacker advertises the block hash via `inv` to the target node.\n4. Target requests the block via `getdata`; attacker serves the poisoned body.\n5. Zebra adds the hash to `non_finalized_block_write_sent_hashes` before validation.\n6. The write task rejects the body at `block_commitment_is_valid_for_chain_history` (auth_data_root mismatch).\n7. The hash is not removed from `non_finalized_block_write_sent_hashes`.\n8. When the valid canonical block arrives (from honest peers or RPC), `queue_and_commit_to_non_finalized_state` sees the hash in the cache and returns `KnownBlock::WriteChannel` duplicate.\n9. The node is stuck at height N-1.\n\nA secondary variant exists where chain pruning (via `MAX_NON_FINALIZED_CHAIN_FORKS`) removes a chain from `chain_set` but leaves its block hashes in `non_finalized_block_write_sent_hashes`, producing the same lockout for children of the pruned fork.\n\n### Patches\n\nPatched in Zebra 4.4.2. The fix removes stale entries from `non_finalized_block_write_sent_hashes` on every failed non-finalized write path.\n\n### Workarounds\n\nThere is no complete configuration-level workaround. Reducing the node's inbound peer count (`network.peerset_initial_target_size`) narrows the attack surface but does not eliminate it. Restarting the node clears the in-memory cache and allows the valid block to be re-fetched.\n\n### Impact\n\nA remote unauthenticated P2P peer can permanently stall a targeted Zebra node at a specific block height. The node diverges from the network tip; downstream consumers (lightwalletd, wallets, explorers, mining infrastructure) relying on the node see a stalled chain. The attack requires winning a propagation race: delivering the poisoned block body before honest peers deliver the canonical block. A well-positioned attacker (low-latency connection to the target, observation of new blocks from other peers) can reliably win this race. In sustained form, the attacker repeats for each new block, keeping the target permanently behind.\n\nRecovery requires restarting the node (which clears the in-memory sent-hash cache) or waiting for a reorg at the affected height (rare on the canonical chain).\n\n### Credit\n\nReported independently by `@ipwning` (primary, with ZIP-244 malleability analysis and zcashd cross-reference) and `@x15-eth` (first reporter, with E2E reproduction and control experiment).",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "crates.io",
      "name" : "zebra-state"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "7.0.0"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 6.0.0"
    }
  }, {
    "package" : {
      "ecosystem" : "crates.io",
      "name" : "zebrad"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "4.5.0"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 4.4.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/ZcashFoundation/zebra/security/advisories/GHSA-4m69-67m6-prqp"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/ZcashFoundation/zebra"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ZcashFoundation/zebra/blob/d4cd662c716382f6397d2a730148025a1ca79fec/zebra-state/src/service.rs#L659-L714"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ZcashFoundation/zebra/blob/d4cd662c716382f6397d2a730148025a1ca79fec/zebra-state/src/service.rs#L797-L802"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-459" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-02T19:43:08Z",
    "nvd_published_at" : null
  }
}