Zcash Metadata Leakage CVE-2019-16930

TLDR: A bug has existed for all shielded addresses since the inception of Zcash and Zcash Protocol. It is present in all Zcash source code forks. It is possible to find the IP address of full nodes who own a shielded address (zaddr). That is, Alice giving Bob a zaddr to be paid, could actually allow Bob to discover Alice’s IP address. This is drastically against the design of Zcash Protocol.

CVE-2019-16930 has been created to track this vulnerability.

PS: This isn’t related to the ITM or “Metaverse Metadata Attack”. That is still coming.

Affected Parties

All people who use zaddrs and who have shared zaddrs with 3rd parties. Such as:

If you have given out your zaddr publicly on social media

If you have given your zaddr in a bug report on Github/email/IRC/etc

If you have ever given a zaddr to an exchange, mining pool or business

If you have ever given a zaddr as Reply-To in a shielded memo

Consider your IP address and geo-location information associated with it as tied to your zaddr. Yeah, this is not good. See Mitigations below.

Unaffected Parties

If you have never used a zaddr, this does not affect you.

If you have only sent funds to other zaddrs, but never received funds, you are safe

If you use Tor/TAILS, then IP metadata leakage is not valuable information to the attacker

Affected Cryptocoins

A non-exhaustive list:

Zcash (ZEC)

Hush (HUSH)

Pirate (ARRR)

All Komodo (KMD) smart chains with zaddrs (enabled by default)

Horizen (ZEN)

Zero (ZER)

VoteCoin (VOT)

Snowgem (XSG)

BitcoinZ (BTCZ)

LitecoinZ (LTZ)

Zelcash (ZEL)

Ycash (YEC)

Arrow (ARW)

Verus (VRSC)

BitcoinPrivate (BTCP)

ZClassic (ZCL)

Anon (ANON)

To clarify, while Bitcoin Gold (BTG) uses the Equihash Proof-of-Work from Zcash, it is not a fork of Zcash source code and it does not use shielded addresss and as such, is not affected.

Additional historical note: KMD previously had zaddrs but disabled that feature, which has now migrated to the Pirate (ARRR) chain. KMD was vulnerable to this in the past, but is no longer, since it no longer supports zaddrs. Safecoin (SAFE) took a similar route, and has disable zaddrs, so they were vulnerable in the past but no longer.

NOTE: The original version of this article was incorrect about SAFE, and the author thanks SAFE devs for the correction.

Mitigations

Firstly, the number one future-proof way to prevent this kind of “metadata leakage attack” is to use Tor with your favorite cryptocoin via -onlynet=onion or even better, via TAILS, which protects you at an even lower level, at the operating system networking layer.

Secondly, with no software updates, affected users can mitigate their risk from publicly “burned” zaddrs. Users must create a brand new wallet.dat, with a brand new zaddr, and then send all funds to that new address. If a user keeps this new zaddr private, it is safe from this attack.

Zcash has released an emergency source code release (no binaries) here.

and described (but not linked to) from this announcement.

Since most users need binaries, every Zcash user and every user of every Zcash fork is vulnerable, even though we have a potential (not verified by 3rd paries) code fix in Zcash Core.

If you do not want people who know your zaddr to know your IP address, I recommend turning off full nodes until they are patched, and/or create fresh new wallets with new zaddrs and discontinue using the old wallets until software updates are released.

Average users can probably stop reading here! Please be safe on the internets.

Recommendations

If you run a mining pool which supports shielded addresses, do not provide a public list of all workers and zaddrs! This severely removes privacy from your miners, due to this current metadata leakage attacks and others. This was common in the past but most mining pools have stopped the practice due to privacy issues.

Never give a zaddr in a bug report on Github! Many tools constantly vacuum potentially sensitive data and OSINT from all public code repositories.

For heightened opsec, if you must give out zaddrs, you can isolate them to one single wallet.dat, which is not normally used and stays off-line, while another wallet with zaddrs can be used to send out funds. Since the sending wallet has never given a zaddr out, this type of leakage is impossible for that node.

Owning The Mistake

I personally apologize to the users of Hush, who I feel responsible to protect. My trust in the Zcash codebase has been shaken to it’s core and I now recommend all Hush users to use Tor/TAILS to protect against future bugs like this coming from Zcash upstream. This bug is fixed to the best of my knowledge in the master branch of Hush and is in the hushd 3.1.1 release. Additionally, the commit fixing the issue was sent to Komodo and merged to their dev branch as well.

I will note that this is still being actively researched and future fixes may be released related to this issue. To learn more about mitigating against this and protecting yourself, if you are a Hush user or another coin, you are welcome to join Hush Discord and ask for help.

Deep Dive

This bug was introduced in the original codebase and Zcash has been vulnerable to this attack it’s entire history, since the commit that introduced it was written in 2016:

commit 369df065836525fd880107a9f5b5c9bc2aa0f890 Author: Sean Bowe <ewillbefull@gmail.com> Date: Wed May 4 17:25:42 2016 Introduce new `libzcash` Zcash protocol API and crypto constructions surrounding the zkSNARK circuit.

That commit added the code to the older Sprout shielded addresses and the code was copied almost exactly for the newer Sapling shielded addresses.

This bug is in the P2P layer of Zcash, inherited from Bitcoin, where nodes exchange data with their peers.

Explanation Of The Attack

Attacker node relays an invalid transaction to the mempool of its peers

This transaction has an invalid serialization of the encrypted memo field

Nodes without the private key and with no viewing key of that shielded address react normally to this invalid transaction

Nodes that have the private key (or viewkey, most likely) will generate a C++ exception

This C++ exception causes different network behavior that perfectly fingerprint the nodes

No record of this attack appears on the blockchain or explorers

The commit fixing the attack is here.

The core of the diff is just a few lines:

- CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << pt.get(); - SaplingNotePlaintext ret; - ss >> ret; + try { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << pt.get(); + ss >> ret; + assert(ss.size() == 0); + } catch (const boost::thread_interrupted&) { + throw; + } catch (...) { + return boost::none; + }

This line not having a try/catch is the core reason for the bug existing:

ss << pt.get();

Since pt is attacker-controlled data being written into a local CDataStream ss object, it needs more careful handling.

One can see now that only exceptions of the type boost::thread_interrupted are bubbled up now, and all other exceptions are “eaten” and boost::none is returned. This makes nodes which have the private key of a zaddr act like all others, preventing the metadata leakage.

The author notes that there still exists vulnerable-looking code for older Sprout addresses here.

One may think that this attack can only be done against a node’s peers, not against the whole network, but it’s trivial to increase the maximum peer count and so to study the entire network with one or a few nodes is completely viable.

An advanced attacker will have a database of zaddrs they want to link to IPs, will run nodes that densely connect to the entire network and periodically send out out invalid transactions to the mempool of all peers, building a history of (timestamp, zaddr, IP address) triplets of data. This data can then be used and linked to other data via amount analysis and timing analsysis to completely de-anonymize shielded transactions and tie them directly to IP addresses and geographical locations.

QED