Terms and Conditions
- I agree to the Grant Agreement terms if funded
- I agree to Provide KYC information if funded above $50,000 USD
- I agree to disclose conflicts of interest
- I understand that this grant program is only eligible for completed work, as it is a retroactive grant program. Applications for planned or partially completed work will not be considered. All completed work will be verified and accepted by its intended users or their representatives, who will confirm that the outputs meet the required quality, functionality, and usability before the work is listed as an option for Coinholder voting.
- I agree that for any new open-source software, I will create a CONTRIBUTING.md file that reflects the high standards of Zcash development, using the
librustzcashstyle guides as a primary reference. - I understand when contributing to existing Zcash code, I am required to adhere to the project specific contribution guidelines, paying close attention to any merge, branch, pull request, and commit guidelines as exemplified in the librustzcash repository.
- I understand all grants are valued in USD but will be disbursed in Shielded ZEC. I acknowledge and accept that disbursement amounts may fluctuate based on the ZEC/USD exchange rate at the time of payment.
Application Owners (@octocat, @octocat1)
@ouicate
Organization or Individual Name
ouicate (Quorint)
Additional Team Members
None. I am applying as an individual.
How did you learn about the Lockbox: Coinholder Retroactive Grants Program?
The Zcash Community Forum: the Q4 2026 call for proposals, and the Q3 2026 retroactive grants for Zebra security findings.
Requested Grant Amount (USD)
$75,000
Category
Research & Development
Project Summary
I found and privately reported two Zebra vulnerabilities in how nodes handle forged block bodies served under genuine block hashes. GHSA-jq45-ghw5-v37x (High), which I reported independently of @x15-eth, let one connected peer hold a syncing node behind the chain tip without ever being banned. GHSA-3c94-hf7p-g5mf (Medium, sole reporter) made nodes verify every proof in blocks that could never be committed. The Zcash Foundation fixed both in released Zebra (7.0.0 and 6.4.0). I am requesting $75,000: half of the $75,000 High base for GHSA-jq45, with x15-eth requesting the other half, plus the $37,500 Medium base for GHSA-3c94.
Project Description
At a glance
- GHSA-jq45 (High, CVSS 7.5): one connected peer could hold a syncing Zebra node behind the chain tip, "never scored, never banned". It affected every Zebra release from 2.3.0 until 7.0.0, and no configuration removed it.
- GHSA-3c94 (Medium, CVSS 5.3): a peer could make a node verify every proof in a forged block body, including the Ironwood Halo2 batch, with no penalty. On the block-sync path, each forged body that won its download race also stalled sync for 67 seconds.
- Credit and fixes: independent reporter on GHSA-jq45, with @x15-eth, fixed in Zebra 7.0.0. Sole reporter on GHSA-3c94, fixed in Zebra 6.4.0.
- Request: $37,500 for GHSA-jq45, which is half of the $75,000 Core Node High base. x15-eth is requesting the other half, the same split coinholders approved for shared advisories in Q3 (#58). Plus $37,500 for GHSA-3c94, the Core Node Medium base. The total is $75,000, at the severities ZF assigned. I am not requesting a bonus, and nothing has been paid for either finding.
The two findings
| Advisory | Severity | Affected | Fixed | My credit |
|---|---|---|---|---|
| GHSA-jq45-ghw5-v37x: Transparent script verification failures are never scored, on the mempool, block-sync and block-gossip paths | High (CVSS 7.5) | zebrad 2.3.0 up to 7.0.0 | Zebra 7.0.0 | Independent reporter, with @x15-eth |
| GHSA-3c94-hf7p-g5mf: Same-hash block body forces full proof verification with no peer penalty | Medium (CVSS 5.3) | zebrad 6.3.0 and earlier | Zebra 6.4.0 (the advisory lists 6.4.2) | Sole reporter |
How they connect
After NU5, a block's hash and Merkle root commit only to transaction IDs, and a v5 transaction's ID doesn't cover its authorizing data (ZIP 244). A peer can therefore take a real block, flip one authorizing byte, and serve a different body under the same hash. Zebra accepts that body as the answer to its own download request.
- GHSA-3c94 (my report, 20 August). Zebra ran every proof in the forged body, including the Ironwood Halo2 batch, before the state caught the mismatch. The serving peer was never scored. On the block-sync path, each forged body that won its download race also cancelled the sync round and idled the node for 67 seconds. The advisory calls a node catching up above the mandatory checkpoint "the most damaging scenario". Zebra 6.4.0 fixed it: the syncer now continues the round and re-requests the body from another peer.
- GHSA-jq45 (x15-eth and me, independently). Zebra scored transparent script failures at 0 on every path. x15-eth reported the issue, and I independently showed the block-sync and block-gossip consequences, and the same-hash construction that reaches them. If you flip a byte in a v5 transaction's
scriptSig, script verification fails before the state's commitment check. As the advisory puts it, the GHSA-3c94 handling therefore "does not apply". The advisory describes the result: "A node syncing above the checkpoint range can be held back by one connected peer. [...] never scored, never banned. No funds, keys, proofs or mining are needed." Zebra 7.0.0 fixed it. Peers that send script-invalid transactions or blocks are now banned on all three paths, which limits the block-sync restart to once per banned address.
What the Zcash Foundation recorded
- GHSA-jq45, reporter field: "@x15-eth; @ouicate (independent rediscovery and further analysis)". Credit section: "Independently reported by @ouicate, who showed the block-sync and block-gossip consequences and the same-hash
scriptSigconstruction." - GHSA-jq45 lists GHSA-3c94 among its references as "the sync-round restart handling this fix must extend".
- GHSA-3c94, credit section: "ouicate, for the finding, the control-flow analysis, and the same-hash construction."
- Zebra 6.4.0 release notes, on the GHSA-3c94 fix: "Thanks to @ouicate for reporting the issue."
Technical Approach (how you did it)
GHSA-3c94 (reported 20 August 2026). I followed a downloaded block on v6.3.0 from the network layer to the state commit, checking four things in turn:
- What the download layer accepts a body by: the header hash only.
- What the semantic verifier binds: txids only.
- Where the authorizing-data commitment is checked: in the state, after every proof has run.
- How the resulting error is scored (0) and how the syncer handles it (a round restart and a 67-second idle).
I then built the same-hash body: an honest block with one authorizing-only byte flipped, which keeps both the header hash and the Merkle root. I reported it privately through a GitHub security advisory, with the code path traced line by line.
GHSA-jq45 (reported 15 October 2026). I checked how script-verification failures reach peer scoring. impl From<BoxError> for TransactionError downcast the shielded signature and proof errors but had no arm for zebra_script::Error, so every transparent script failure collapsed to InternalDowncastError, which scores 0. Zebra's own tests asserted that collapse. I then extended the same-hash construction to it. Flipping one byte of a v5 transaction's scriptSig keeps the block hash and the Merkle root, because ZIP 244 leaves scriptSig out of the txid, but script verification fails before the state's commitment check, so the GHSA-3c94 handling does not apply. On block sync the serving peer went unscored, the round restarted, the hash was dropped and the syncer idled for 67 seconds. On block gossip the peer also went unscored. I reported it privately with a runnable unit test for the root cause and network-level reproductions.
Tools: I used LLM-assisted code review to map the code paths and generate leads, and confirmed both findings by hand with tests before reporting.
Time Period of Work Completion
August to October 2026. I reported GHSA-3c94 on 20 August; it was fixed in Zebra 6.4.0 (23 September) and published on 28 September. I reported GHSA-jq45 on 15 October; it was fixed in Zebra 7.0.0 and published on 22 October.
Total Budget (USD)
0
Budget Breakdown
This is a retroactive request for completed work, not a reimbursement of costs.
Previous Funding
No
Previous Funding Details
I have not received funding from the Coinholder Grants Program.
Other Funding Sources
No
Other Funding Sources Details
I have not received, and do not expect, payment from any other source for these two advisories. Both were reported after ZCG's bounty program closed on 26 May 2026. While that program was open, ZCG paid for two of my earlier Zebra reports (public record). Those are separate findings and are not part of this request.
Success Metrics
Quantitative
- 2 published advisories crediting me: GHSA-jq45 (High 7.5, independent reporter) and GHSA-3c94 (Medium 5.3, sole reporter).
- 2 fixes shipped: Zebra 6.4.0 and Zebra 7.0.0.
- 3 paths (mempool, block sync and block gossip) that now ban peers sending script-invalid data. Before 7.0.0, none of them scored those peers.
- 67 seconds of forced sync idle per forged body that won its download race. 6.4.0 removed this for the commitment case. 7.0.0 limits it to once per banned address for the script case.
- Exposure closed for every zebrad release from 2.3.0 until 7.0.0 (GHSA-jq45), and for every release up to 6.3.0 (GHSA-3c94).
Qualitative
- Liveness for the nodes most exposed during an attack: nodes catching up above the checkpoint, such as new nodes, and exchanges and explorers coming back from downtime.
- A free, unauthenticated way to hold a node behind the tip is gone.
- The same-hash attack is now penalized on every path, not only in the commitment case that GHSA-3c94 fixed.
Proof of completion
Repository/Commit
- GHSA-jq45 fix at 7.0.0: https://github.com/ZcashFoundation/zebra/blob/e8db87eb69254e0d4eedd058eadd48bdfa09065a/zebra-consensus/src/error.rs#L356-L370
- GHSA-3c94 fix, described in the Zebra 6.4.0 release notes: https://github.com/ZcashFoundation/zebra/releases/tag/v6.4.0
Publication
- GHSA-jq45-ghw5-v37x: https://github.com/ZcashFoundation/zebra/security/advisories/GHSA-jq45-ghw5-v37x
- GHSA-3c94-hf7p-g5mf: https://github.com/ZcashFoundation/zebra/security/advisories/GHSA-3c94-hf7p-g5mf
Deployment/Release
- Zebra 7.0.0: https://github.com/ZcashFoundation/zebra/releases/tag/v7.0.0
- Zebra 6.4.0 ("Thanks to @ouicate for reporting the issue"): https://github.com/ZcashFoundation/zebra/releases/tag/v6.4.0
Other Evidence
- Q3 precedent for splitting a shared advisory: https://github.com/Financial-Privacy-Foundation/ZcashCoinholderGrantsProgram/issues/58
- ZCG's severity framework and payout schedule: https://forum.zcashcommunity.com/t/zcg-security-vulnerability-disclosure-initiative/55545
Conflict of Interest Disclosure
@x15-eth, the other reporter on GHSA-jq45, is requesting the other half of it in a separate application this round. We agreed to split it equally. I am also applying separately for two Critical Zebra consensus vulnerabilities, in Two Critical Zebra Consensus Vulnerabilities (#77). Apart from that, I have no employment, contracting or governance relationship with the Zcash Foundation, ZCG, FPF, or any keyholder organization.
Community Forum Posting
- I understand it is my responsibility to post a link to this issue on the Zcash Community Forums after this application has been submitted so the community can give input. I understand this is required in order for the community to discuss and vote on this grant application. Note: If you are unable to post on the forum (for example, due to new user restrictions), please leave a comment below, and we will adjust your posting permissions.