Several Zcash Vulnerabilities Successfully Remediated

By: Neal Jayu [*], Daira-Emma Hopwood [*], Kris Nuttycombe [*], Zooko Wilcox [†]

[*] Zcash Open Development Lab, [†] Shielded Labs

Key Points

  • Several vulnerabilities in zcashd and Zebra were discovered and patched, including a bug that could crash nodes handling certain Orchard transactions, a consensus enforcement gap between the two implementations that could have triggered a chain fork, a bug that could disable enforcement of zcashd’s turnstile accounting, and undefined behavior due to unchecked integer arithmetic in pool balance calculations.
  • The vulnerabilities were not exploited to affect the consensus chain. All user funds are safe, and user privacy was not at risk.
  • None of these vulnerabilities could by themselves have been used to inflate the ZEC supply. The vulnerability that could have been used to disable the turnstile mechanism is not independently exploitable without a separate, additional counterfeiting vulnerability. In that case, any on-chain turnstile violation would have been publicly detectable.
  • The vulnerabilities were disclosed via our coordinated disclosure process by security researcher Alex “Scalar” Sol – the same researcher who reported the March 2026 Sprout verification vulnerability – on April 4, 2026. Patches for zcashd, which also address additional possible exploitation vectors, were developed by engineers at Zcash Open Development Lab (ZODL). A patch for Zebra was developed by engineers at the Zcash Foundation.
  • Both zcashd and Zebra required patches and were updated in coordination before public disclosure.
  • Mining pools representing a supermajority of the network’s hash power, and the primary operator running Zebra in mining production, deployed patches prior to this disclosure.
Affected Versions Fixed Version
zcashd v5.0.0 through v6.12.0 v6.12.1
Zebra v1.0.0 through v4.3.0 v4.3.1

Summary

Security researcher Alex “Scalar” Sol identified a set of vulnerabilities across both zcashd and Zebra, the two full-node implementations of the Zcash protocol. Engineers at Zcash Open Development Lab (ZODL) and the Zcash Foundation developed and reviewed fixes for both implementations, and coordinated their deployment with mining pools and node operators before this public disclosure.

The vulnerabilities were not exploited to affect the consensus chain. All user funds are safe, and user privacy was not at risk. You can verify this by running either Zebra v4.3.1 or zcashd v6.12.1: both updated implementations independently validate the Zcash blockchain and confirm that only valid transactions have been added to the chain.

The most directly exploitable of the bugs, present since zcashd v5.0.0, was a crash: a crafted Orchard transaction could cause any reachable zcashd or Zebra node to panic and terminate. We confirmed that no transactions triggering this condition exist on mainnet, and no unexplained node crashes were reported prior to the fix. A related bug – a discrepancy between zcashd and Zebra’s enforcement of an Orchard protocol requirement – could have been used to publish a transaction that zcashd would accept and Zebra would reject, forcing a chain fork.

A separate bug in zcashd, introduced in v5.10.0, could disable Zcash’s turnstile accounting – the mechanism that tracks and enforces ZEC balances between value pools. This could happen under certain network conditions that could arise from ordinary P2P operation, or could be triggered deliberately by a malicious peer. This bug is not independently exploitable to create invalid Zcash transactions; exploiting it to steal funds would require a separate, independent balance vulnerability on top of it. Also, any turnstile violation would be publicly observable as an anomaly, with a rollback available as a remedy. No such event occurred.

A potential issue in zcashd, related to the preceding one, that could have caused on-disk chain accounting values to be corrupted by a malicious peer has also been remediated.

In the course of addressing these vulnerabilities, ZODL engineers also added hardening to pool balance arithmetic to prevent undefined behavior in edge cases where a maliciously crafted block could trigger signed integer overflow in C++, and improved exception safety in the case of overflow detection.

This is the second set of Zcash vulnerabilities disclosed within a month. We believe the pattern here is a good one: a researcher returning with more findings, again reporting via our coordinated disclosure channels. The breadth and thoroughness of this investigation —spanning two implementations and surfacing four distinct issues in the span of a week— reflects the kind of serious security scrutiny a financial protocol should receive and that the Zcash ecosystem is increasingly capable of handling. The organizations involved work in close coordination, and the response here demonstrates that.

Background

Zcash maintains several shielded value pools: Sprout (deprecated), Sapling, and Orchard. Each is protected by zero-knowledge proofs that ensure only valid transactions can move ZEC and that new ZEC is only created by block reward issuance. Transparent funds and the lockbox are each also considered to be value pools.

Orchard is Zcash’s current shielded pool, introduced with the NU5 network upgrade in May 2022. Two of the vulnerabilities in this report involve how specific transaction fields within Orchard actions are validated by the consensus rules.

A foundational safety mechanism in the Zcash protocol is the turnstile: an accounting constraint that tracks the total ZEC balance in each value pool (Sprout, Sapling, Orchard, transparent, and lockbox) and limits how much value can flow between pools. Turnstiles are specified in ZIP 209 and section 4.17 of the Zcash protocol specification. They function as blast doors: even if a vulnerability were used to counterfeit value within a pool, any attempt to move excess total value to another pool would be prevented. That is, they act as a second line of defence for balance properties that the protocol is intended to enforce. This is essential to understanding the turnstile-related vulnerability in this report — and why it does not represent an independent threat to user funds.

Third Party Disclosure

Alex “Scalar” Sol reported the vulnerabilities to Shielded Labs on April 4, 2026.

Following receipt of the report, Shielded Labs coordinated with ZODL core engineers to validate the issues and develop patches. Daira-Emma Hopwood and Kris Nuttycombe analyzed and validated the vulnerabilities. Kris Nuttycombe authored the patches addressing the Orchard crash, the consensus enforcement gap, and the turnstile accounting bug; Daira-Emma Hopwood authored two hardening patches addressing integer overflow and exception safety, and additional changes to introduce a chain supply checkpoint and recompute chain value deltas. Daira-Emma Hopwood and Kris Nuttycombe reviewed each other’s work, and Zooko Wilcox reviewed and commented extensively on the patch set that was provided to partners. We have started to use AI to a greater extent in our work; parts of the vulnerability analysis and remediation were assisted by Claude Opus 4.6, with thorough human review.

Shielded Labs disclosed the issues to the Zcash Foundation on April 6, 2026. Conrado Gouvêa of the Zcash Foundation authored the Zebra patch addressing the Orchard crash vulnerability and the related enforcement discrepancy.

Shielded Labs led outreach to mining pools and infrastructure providers to coordinate patch deployment. Mining pools including ViaBTC, Luxor, F2Pool, and AntPool – which run zcashd – were contacted directly to coordinate upgrades. Foundry, which runs Zebra in mining production, was also contacted and received the Zebra patch prior to public release.

The zcashd patch set sent to mining pools was intentionally kept simple to minimize the risk of regressions. The changes to zcashd in v6.12.1 have these patches and also a more extensive set of hardening changes and tests, including the addition of a chain supply value checkpoint at NU6.1 activation, and recomputation of the deltas after that point to allow any corruption of the persisted values to be detected.

Timeline

May 21, 2019

  • ZIP 209’s consensus-level turnstile enforcement deployed with zcashd’s v2.0.5 release.

June 14, 2023

  • Zebra v1.0 released.

August 27, 2024

  • Vulnerability that could disable turnstile enforcement and allow integer overflow (Undefined Behavior) introduced with zcashd’s v5.10.0 release.

April 4, 2026

  • Alex “Scalar” Sol reported the vulnerabilities to Shielded Labs.

April 5, 2026

  • Shielded Labs met with ZODL engineers Daira-Emma Hopwood and Kris Nuttycombe to review and validate the vulnerabilities.

April 6, 2026

  • Shielded Labs disclosed the issues to the Zcash Foundation.
  • Patch development began. Kris Nuttycombe authored the initial patches addressing the Orchard crash vulnerability and the turnstile accounting bug.

April 10, 2026

  • Daira-Emma Hopwood completed two hardening patches addressing integer overflow undefined behavior and exception safety.
  • Conrado Gouvêa (Zcash Foundation) completed the Zebra patch.
  • Patches were provided to ecosystem partners.

April 11, 2026

  • Mining pool F2Pool deployed the zcashd patch.

April 16, 2026

  • Mining pools Luxor and Foundry confirmed deployment of the zcashd and Zebra patches, respectively.

April 17, 2026

  • Mining pool ViaBTC confirmed deployment of the zcashd patch.
  • zcashd v6.12.1 and Zebra v4.3.1 released publicly.

Vulnerability Details

Orchard Node Crash via Identity Randomized Key

The Orchard shielded protocol includes a re-randomizable signing key, rk, in each transaction action. The Zcash protocol specification permits rk to take any value in the Pallas group, including the identity point. However, the implementation of Orchard proof verification in both zcashd and Zebra would panic when it encountered the identity rk during proof instance construction – causing both nodes to crash.

An attacker could exploit this by broadcasting a crafted transaction with an Orchard action having an all-zeros rk encoding (the only encoding of the Pallas identity point). Any zcashd or Zebra node that received and processed such a transaction would crash immediately. A sustained attack replaying this transaction could prevent nodes from participating in the network.

Both implementations were independently patched:

  • zcashd: Kris Nuttycombe added a validate_action_encodings() check in CheckTransactionWithoutProofVerification, which runs before proof verification in both the mempool and block validation paths. The check compares rk against its all-zeros canonical encoding and rejects any transaction containing such a value with a DoS(100, REJECT_INVALID) penalty.
  • Zebra: Conrado Gouvêa (Zcash Foundation) added a rejection at deserialization time, returning a serialization error if rk encodes the identity point.

The protocol specification will be updated to bring it in line with the implementations. (It is usually safer in cases of divergence between implementations and the specification to bring the laxer one in line with the stricter one, so that the effect on consensus is at most a soft fork.)

No transactions containing identity rk values were found on the Zcash mainnet, confirming this vulnerability was not exploited on the consensus chain before the fix.

Consensus Enforcement Gap: Identity Ephemeral Key

The Zcash protocol specification (§5.4.9.4) requires that the ephemeral public key epk in each Orchard action encodes a non-identity point on the Pallas curve. Zebra enforces this requirement at deserialization time. zcashd did not enforce it at all.

This discrepancy created a potential consensus split: a crafted transaction containing an identity epk would be accepted by zcashd but rejected by Zebra. An attacker publishing such a transaction could cause zcashd and Zebra nodes to disagree about the validity of a block, chain-forking the network.

The patch addressing the rk crash vulnerability simultaneously closes this gap: the validate_action_encodings() function now checks both rk and epk, bringing zcashd into full alignment with Zebra’s implementation and the protocol specification. No identity epk values were found on mainnet.

Turnstile Accounting Bypass via Duplicate Block Header

Zcash’s turnstile mechanism tracks the total ZEC balance in each value pool (Sprout, Sapling, Orchard, transparent, and lockbox), and enforces invariants on how much value can flow between pools. These checks run during block connection and require the per-pool chain balance to be known. When a pool’s chain balance is nullopt – meaning its value is not being tracked – the corresponding turnstile check is skipped.

A bug in how zcashd initializes pool balances during block processing meant that receiving a block whose header matched a previously seen block would silently overwrite the pool balance fields for that block index entry with nullopt. This would disable turnstile enforcement for all subsequent blocks. Critically, receiving duplicate block announcements from multiple peers is routine P2P network behavior, which implies that this condition could arise in ordinary operation, not only under deliberate attack. Under deliberate attack, a malicious peer could replay any already-accepted block header to reset pool accounting, without needing to be a miner or have any special access.

The root cause was a misplaced function call. SetChainPoolValues —which initializes per-pool delta fields and resets chain balance fields to nullopt in preparation for later accumulation— was called in AcceptBlock before the check for whether the block header had already been seen. The fix moves this call to ReceivedBlockTransactions, which is reached only for genuinely new blocks and only after Merkle root validation, ensuring that pool accounting is never reset by replayed or duplicate headers.[1]

This bug is not independently exploitable to create or steal ZEC. Bypassing the turnstile check does not itself allow balance violations — it only means that a separate, independent balance vulnerability could potentially cause harm across multiple pools rather than being contained to one. Moreover, had such a combination been exploited, any turnstile-violating blocks would have been rejected by Zebra nodes —as well as by newly synced or reindexed zcashd nodes— resulting in a publicly visible chain fork. No such event occurred.

zcashd also had a related issue that could have allowed a malicious peer to corrupt the on-disk chain accounting values — specifically, the deltas between “before” and “after” chain balances at a given block. The impact would be that after a restart, the turnstile could trigger at the wrong level relative to the correct bounds.

If the attack described earlier were used to corrupt in-memory delta values in the block index, the accumulated chain value would normally be set to nullopt, meaning that the corrupt values could not directly be observed (except via the getblock RPC on specific blocks) and would not persist when the node is restarted. However, under some circumstances the incorrect deltas could be persisted to disk. The fix adds a checkpoint for these values at NU6.1 activation, and recomputes the deltas after that point from block data. If the persisted values are inconsistent with the recomputed ones past the checkpoint, zcashd v6.12.1 onward will abort on start-up and require a reindex, which corrects the on-disk values. (It is sufficient for turnstile enforcement that they are correct in the main chain past NU6.1; earlier deltas can only be observed via the getblock RPC and are not otherwise used.) Note that this defence against past corruption cannot be applied to nodes that enabled pruning (which is not the default or a common setting in the Zcash ecosystem). Users of pruned nodes should instead check that the chain pool values returned by the getblockchaininfo RPC match those of an unpruned node at the same block.

Turnstiles are an important layer of defense, and their correct operation is central to the protocol’s security guarantees. We treat any condition that disables them – even one that is not independently exploitable – as a high-priority fix.

Integer Overflow Hardening in Pool Balance Accounting

In C++, signed integer overflow is undefined behavior: the compiler is permitted to assume it cannot occur and may optimize surrounding code under that assumption. In zcashd’s pool balance accumulation routines, it was possible to construct a maliciously-crafted block that triggered signed integer overflow during the computation of per-pool value deltas. Depending on the compiler and its optimization settings, this undefined behavior could potentially have caused turnstile checks to be skipped, consensus validation to return early, or pool balance values to be computed incorrectly.

Daira-Emma Hopwood addressed this with two patches. The first adds explicit range checks using a new MoneyDeltaRange helper – verifying that running sums of per-pool deltas remain within [-MAX_MONEY, MAX_MONEY] at every accumulation point in SetChainPoolValues, ReceivedBlockTransactions, ConnectBlock, and LoadBlockIndexDB. These checks also guard against corrupted on-disk balance data that may have been written by a prior vulnerable version. The second patch wraps all consensus-relevant calls to value computation functions in explicit exception handlers, ensuring that any out-of-range value produces a proper DoS(100, REJECT_INVALID) consensus rejection rather than an unhandled exception.

Acknowledgements

Thanks to Alex “Scalar” Sol for discovering these vulnerabilities and responsibly disclosing them to protect Zcash users – his second such disclosure to the Zcash ecosystem.

Special thanks to Kris Nuttycombe and Daira-Emma Hopwood at ZODL for their thorough analysis, prompt patch development, and careful review of each other’s work.

Thanks to Conrado Gouvêa at the Zcash Foundation for the Zebra patch and for the Foundation’s coordination throughout the process.

Thanks also to Judah Caruso at Shielded Labs, who worked closely with mining pools, CEXs, and other infrastructure providers to ensure they and Zcash were protected.

We extend our thanks to ViaBTC, Luxor, F2Pool, and Foundry for their deployment of patches ahead of public disclosure.

18 Likes

What stands out is how they cluster around edge cases, cross-implementation differences, and state handling. The orchard crash, the EPK inconsistency, and especially the turnstile bypass all feel like examples of “locally correct, globally fragile” logic. Nothing breaks in isolation, but interactions create risk.

The Zcashd vs Zebra divergence is particularly important. Having two implementations is a strength but only if they cover tightly at the consensus boundary. Otherwise you’re effectively carrying latent fork risk.

Also interesting to see how many of these issues relate to initialisation and reprocessing paths rather than core proof logic. That suggests the zk layer is holding up well, but the surrounding systems still need constant hardening.

Overall, this feels more like the system is entering a phase of deeper, adversarial scrutiny, which is where a protocol like this needs to be. :smile:

2 Likes

Unfortunately zcashd and zebra are not really “two implementations”, but the former is deprecated in favour of the latter. I think zcash really needs two independent implementations based on different languages, stack, foundation libraries. But the second one has yet to be born.

2 Likes

They are two seperate implementations. Zebra was built from scratch in Rust, zcashd is a fork of the C++ bitcoin codebase, nothing about them implementation-wise is really cross compatible at all. Zebra was created to be the second node because at that time, the idea to deprecate zcashd was not planned. Having another node implementation would be nice as long as you have a team completely committed to maintaining it like zebra is maintained by the zcash foundation. Otherwise it will ultimately just cause problems.

3 Likes

Yeah, that’s a fair correction. “Two implementations" only really holds if they’re independently evolving consensus clients, and we are clearly converging toward Zebra as the canonical path.

If anything, that raises the stakes a bit. During this transition phase, you still get some of the benefits of cross-implementation comparison (like catching consensus edge cases), but you’re also reducing long-term diversity.

And I agree real client diversity is different parsing logic, different state models, and different failure modes. That’s usually where consensus bugs get surfaced, especially around serialisation, validation ordering and state transitions like the turnstile logic here.

Right now it feels like Zebra is still being hardened into that role, but long-term, relying on a single dominant implementation probably isn’t where we want to end up.

1 Like

Now that we have very good AI, like Claude, it should be way easier to keep maintaining zcashd. For the sake of security it would be great to have two different clients.

Edit: I also suspect that we have more bugs, that have been patched in Bitcoin Core, lingering in zcashd: https://bitcoincore.org/en/security-advisories#past-security-advisories

2 Likes