GHSA-cwfq-rfcr-8hmp: Zebrad Zebra's Transparent SIGHASH_SINGLE Handling Diverges Vulnerability
CRITICALGHSA-cwfq-rfcr-8hmp is a CRITICAL zebra's transparent sighash_single handling diverges vulnerability (CWE-573: Improper Following of Specification by Caller) affecting Zebrad libraries in the crates.io ecosystem. The issue involves zebra's transparent sighash_single handling diverges from zcashd for corresponding outputs. Remediation is available by upgrading affected packages to version 4.4.0.
GHSA-cwfq-rfcr-8hmp at a Glance
How to Fix GHSA-cwfq-rfcr-8hmp
To remediate this vulnerability, upgrade all affected Zebrad dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade zebrad to 4.4.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for zebrad
How to Check if You Are Affected
To determine whether your application is affected by GHSA-cwfq-rfcr-8hmp, inspect your project dependencies for Zebrad:
inspect dependencies for zebrad
Analyze the terminal output to locate any version of Zebrad in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.4.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-573: Improper Following of Specification by Caller, where the product does not follow or incorrectly follows the specifications as required by the implementation language, environment, framework, protocol, or platform. In Zebrad, 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
GHSA-cwfq-rfcr-8hmp Affected Zebrad Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
zebrad
|
crates.io |
< 4.4.0
|
4.4.0
|
References
Frequently Asked Questions
What is GHSA-cwfq-rfcr-8hmp?
Which Zebrad versions are affected?
What version fixes GHSA-cwfq-rfcr-8hmp?
Is zebrad affected?
What CWE is associated with GHSA-cwfq-rfcr-8hmp?
How can I check crates.io dependencies for GHSA-cwfq-rfcr-8hmp?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-cwfq-rfcr-8hmp",
"modified" : "2026-05-07T21:02:30Z",
"published" : "2026-05-07T21:02:30Z",
"aliases" : [ ],
"summary" : "Zebra's Transparent SIGHASH_SINGLE Handling Diverges from zcashd for Corresponding Outputs",
"details" : "# `Zebra` Transparent `SIGHASH_SINGLE` Corresponding-Output Handling Diverges From `zcashd`\n\n### Summary\nFor V5+ transparent spends, `Zebra` and `zcashd` disagree on the same consensus rule: `SIGHASH_SINGLE` must fail when the input index has no corresponding output. `zcashd` treats this as consensus-invalid under ZIP-244, while `Zebra`'s transparent verification path computes a digest for the missing-output case instead of failing.\n\nThe result is a direct block-validity split. A malformed V5 transparent transaction can be accepted by `Zebra`, retained in `Zebra`'s mempool, selected into `Zebra` `getblocktemplate`, mined into a block, and then rejected by `zcashd`.\n\n### Details\nValidated code revisions used during analysis:\n\n- `zcashd`: `2c63e9aa08cb170b0feb374161bea94720c3e1f5`\n- `Zebra`: `a905fa19e3a91c7b4ead331e2709e6dec5db12cb`\n\nScope note:\n\n- earlier triage material grouped pre-V5 and V5 behavior together;\n- re-execution on the pinned revisions did not reproduce the claimed pre-V5 / V4 reject-side behavior;\n- this advisory therefore covers the V5+ / ZIP-244 variant only.\n\n`zcashd` side:\n\n- Transparent scripts in blocks are checked through `TransactionSignatureChecker::CheckSig()` and `SignatureHash()`: [`zcash/src/script/interpreter.cpp`](https://github.com/zcash/zcash/blob/2c63e9aa08cb170b0feb374161bea94720c3e1f5/src/script/interpreter.cpp#L1386-L1407).\n- In the ZIP-244 branch, `SignatureHash()` explicitly throws when `SIGHASH_SINGLE` or `SIGHASH_SINGLE|ANYONECANPAY` is used with `nIn >= txTo.vout.size()`: [`zcash/src/script/interpreter.cpp`](https://github.com/zcash/zcash/blob/2c63e9aa08cb170b0feb374161bea94720c3e1f5/src/script/interpreter.cpp#L1221-L1259).\n- `CheckSig()` catches that exception and returns `false`, causing the transparent script to fail.\n\n`Zebra` side:\n\n- V5 transparent inputs route into the same FFI-based transparent script verifier used for block validation: [`zebra/zebra-consensus/src/transaction.rs`](https://github.com/ZcashFoundation/zebra/blob/a905fa19e3a91c7b4ead331e2709e6dec5db12cb/zebra-consensus/src/transaction.rs#L989-L1098).\n- `Zebra` converts the decoded hash type and asks its Rust sighash engine for a digest without adding the corresponding-output pre-check that `zcashd` enforces first: [`zebra/zebra-script/src/lib.rs`](https://github.com/ZcashFoundation/zebra/blob/a905fa19e3a91c7b4ead331e2709e6dec5db12cb/zebra-script/src/lib.rs#L160-L175), [`zebra/zebra-chain/src/primitives/zcash_primitives.rs`](https://github.com/ZcashFoundation/zebra/blob/a905fa19e3a91c7b4ead331e2709e6dec5db12cb/zebra-chain/src/primitives/zcash_primitives.rs#L307-L343).\n- `Zebra` forwards canonical `SIGHASH_SINGLE` into the Rust ZIP-244 implementation.\n- In that implementation, when `input.index() >= bundle.vout.len()`, the code uses `transparent_outputs_hash::<TxOut>(&[])` instead of erroring: [`zcash_primitives/src/transaction/sighash_v5.rs`](https://github.com/zcash/librustzcash/blob/c3425f9c3c7f6deb20720bb78b18f35fbbed8edd/zcash_primitives/src/transaction/sighash_v5.rs#L101-L107), [`zcash_primitives/src/transaction/sighash_v5.rs`](https://github.com/zcash/librustzcash/blob/c3425f9c3c7f6deb20720bb78b18f35fbbed8edd/zcash_primitives/src/transaction/sighash_v5.rs#L131-L139).\n\nWhy this is exploitable:\n\n- the malformed transaction only needs fewer transparent outputs than inputs;\n- the attacker signs the digest that `Zebra` computes for the missing-output case;\n- `Zebra` then sees a valid transparent signature, while `zcashd` never reaches the same digest because it fails first.\n\nOrdinary path viability:\n\n- `zcashd` ordinary mempool admission is not the practical trigger path, because the same ZIP-244 `SignatureHash()` checks fail there first: [`zcash/src/main.cpp`](https://github.com/zcash/zcash/blob/2c63e9aa08cb170b0feb374161bea94720c3e1f5/src/main.cpp#L1981-L1995), [`zcash/src/script/interpreter.cpp`](https://github.com/zcash/zcash/blob/2c63e9aa08cb170b0feb374161bea94720c3e1f5/src/script/interpreter.cpp#L1221-L1259).\n- `Zebra` ordinary mempool admission is viable because `Zebra` uses the same transparent verifier for mempool and block validation and does not have a separate \"one output per input\" standardness rule here: [`zebra/zebra-consensus/src/transaction.rs`](https://github.com/ZcashFoundation/zebra/blob/a905fa19e3a91c7b4ead331e2709e6dec5db12cb/zebra-consensus/src/transaction.rs#L414-L519), [`zebra/zebrad/src/components/mempool/storage.rs`](https://github.com/ZcashFoundation/zebra/blob/a905fa19e3a91c7b4ead331e2709e6dec5db12cb/zebrad/src/components/mempool/storage.rs#L255-L376).\n- `Zebra` is a block-template producer, so the realistic stock path is `Zebra` mempool -> `Zebra` `getblocktemplate` -> external miner: [`zebra/zebra-rpc/src/methods/types/get_block_template/zip317.rs`](https://github.com/ZcashFoundation/zebra/blob/a905fa19e3a91c7b4ead331e2709e6dec5db12cb/zebra-rpc/src/methods/types/get_block_template/zip317.rs#L72-L105).\n\n### PoC\nValidated commits:\n\n- `zcashd`: `2c63e9aa08cb170b0feb374161bea94720c3e1f5`\n- `Zebra`: `a905fa19e3a91c7b4ead331e2709e6dec5db12cb`\n\nManual reproduction steps:\n\n1. Build an otherwise-valid V5 transaction with at least two transparent inputs and only one transparent output.\n2. Sign input `0` normally.\n3. Sign input `1` with canonical `SIGHASH_SINGLE` or `SIGHASH_SINGLE|ANYONECANPAY`.\n4. Use the digest returned by `Zebra`'s ZIP-244 path, where the missing output contributes `transparent_outputs_hash([])`.\n5. Submit the transaction to `Zebra` and to `zcashd`.\n6. Observe:\n - `Zebra` accepts it into the mempool;\n - `Zebra` selects it into `getblocktemplate`;\n - `Zebra` can mine and accept a block containing it;\n - `zcashd` rejects it in the ordinary mempool path.\n\n### Impact\nThis is a direct V5+ transparent consensus split.\n\nWho can trigger it:\n\n- an ordinary transaction author can craft the malformed V5 transparent transaction;\n- the accept-side stock path is `Zebra`'s mempool and block-template path;\n- an external miner still has to include the transaction in a block for the split to materialize.\n\nWho is impacted:\n\n- `Zebra` can accept and template a transaction / block that `zcashd` rejects;\n- this makes the issue both a consensus-divergence problem and a practical `Zebra` block-template safety problem.",
"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:H/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "crates.io",
"name" : "zebrad"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "4.4.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/ZcashFoundation/zebra/security/advisories/GHSA-cwfq-rfcr-8hmp"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/ZcashFoundation/zebra"
}, {
"type" : "WEB",
"url" : "https://github.com/ZcashFoundation/zebra/releases/tag/v4.4.0"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-354", "CWE-573" ],
"severity" : "CRITICAL",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-07T21:02:30Z",
"nvd_published_at" : null
}
}