Securing ZK Applications | Tim Becker - ChainLight
Ethereum Denver·Thu, Oct 9, 2025, 12:00 AM
Zero Knowledge Proofs enable novel applications by offering succinct and privacy-preserving verification of arbitrary computations, leading to their rapid adoption in blockchains. However, this new and powerful technology comes with many new security risks. In this talk, we give a comprehensive overview of ZK application security, detail specific security bugs we have found in real-world projects, and suggest ways to mitigate these new dangers.
Transcript
[Music] okay hi everyone thanks for being here um my name is Tim Becker I'm a security researcher at chainlight and today I'm going to talk about ZK security so I'm going to start with a very quick overview of zero knowledge proofs just to get everyone on the same page so there's two parties approver and a verifier and um X denotes some public data that the verifier is going to see that some property about is being proven uh W is witness data so this is some supplemental data used by the prover to verify that property about X but uh is not necessarily disclosed to the verifier and uh the way that it works is the prover takes the public input and the witness data and generates a proof Pi that uh the verifier can then verify against the public input and either reject or accept the proof and all proof systems pretty much in all of computer science need to have two properties completeness and soundness so completeness says that all true statements that you care to prove can be proven and soundness says that only true statements that you care to prove can be proven and the Magical properties of zero knowledge proofs that lead to all of the amazing applications are succinctness and soundness so in short succinctness says that the proof is very small and very cheap for the verifier to verify so they need to do a lot less work than the prover and the zero knowledge property of course is you know where it deres the name and it says that the proof doesn't reveal any unnecessary information about the witness so the witness is this data that the verifier doesn't see and ideally they should not be able to recover any information about it other than exactly what is necessary for the proof so how do some examples fit into this in a layer two blockchain the prover is usually the sequencer or some party associated with the sequencer and the verifier is an L1 contract and essentially they're proving the block transition function so the public data are essentially two consecutive block roots and the witness data is some validation Trace like the trace of a program that validates the block transition function uh and soundness here guarantees that only valid blocks can be uh finalized on the L1 so the L1 isn't going to have some incorrect view of the state of the L2 and completeness says that all valid blocks so anything that can happen on the L2 can actually be proven by the prover and uh finalized on L1 um another example uh this is like privacy preserving identity so um say your application wants to verify a user is at least 21 years old and the public input here is some like pseudonymous user identity like a username or something and the witness data is some assigned data that attests to their age so for example a passport um contains an NFC C chip that transmits assigned payload uh testing to a bunch of facts about your identity and you can verify that in a zero knowledge proof and withhold all of that sensitive information about you except for the fact that you're over 21 so here soundness guarantees that only users that actually have this signed at testation can authenticate to your application and zero knowledge protects the user from revealing information about themselves that uh they don't want to reveal so what can go wrong with zero knowledge proofs I have three threat models to talk about one is um input basically leaking of this private input data so in the previous application this would mean that an attacker can recover more information about the user than just their age using this proof data and here the attacker can either be the verifier themselves or some other third party that can see the same data as the verifier uh another threat model is malicious input so in this model um the prover is not in full control of this public data that they're proving information about so like a sequencer isn't in full control of the transaction data because users can submit transactions to the layer 2 that the prover is going to have to be able to prove so if an attacker can produce some transaction that they know the proof system won't be able to handle then they can break completeness because the appr won't be able to approve that and uh this can cause like a denial of service on a layer two as an example and the third threat model is a malicious prover so in this case it's either the prover themselves like in uh an authentication application anyone can be aover um if it's like a sequencer even if it's a permissioned prover um this model can assume that that prover was compromised somehow like maybe their signing key that they used to relay the proofs was uh extracted and the pro the attacker can now create proofs and relay them to the layer one so in this model the attacker is trying to break soundness and convince the verifier of some fact that is not true yeah so to see how these things can actually go wrong we need to look inside a ZK proof system and I think it's conceptually helpful to divide it into two pieces the front end and the back end so the front end is essentially the part that the developer of the ZK application interacts with so this can be circuit libraries where they write ZK circuits um or it can be these days higher level ZK VMS so these are essentially like interfaces that will allow you to write regular like rust code and compile them to a virtual machine that then gets proven using some underlying zero knowledge circuits and the back end is essentially what takes these constraints that get outputed from the front end and does the the cryptographic magic necessary to actually prove it so there's a a bunch of proof systems um I'm sure you've all heard of plon but uh there's a ton of variants there's new proof systems coming out all the time and these are essentially what the backend implements and yeah bugs can happen at any layer of the stack so they can be in your application the front end or the back end so front end bugs are often overlooked but they can be quite critical and these can happen where even if you wrote your application code correctly uh the front end compile them into constraints incorrectly or in the case of like a ZK VM it can mean that the circuits that verify the Integrity of the VM computation have a bug and um I also include standard Library vulnerabilities in the front-end bugs because even though standard libraries are kind of like application code that use the front end they are usually packaged with it and part of the same trust assumptions that you're making as a developer when you choose a front end and there's a famous example of a uh frontend or a standard Library vulnerability in circom lib where their mimc hash function had a soundness bug where a variable was assigned but not constrained and this actually made tornado cash vulnerable to double spend attacks and this vulnerability was actually self- exploited by the tornado cache team to safeguard the funds and upgrade the protocol uh there can also be bugs in the back end so the back end is generally just implementing a proof system and one crucial detail that can go wrong in these proof systems is something called the Fiat Shamir cisic so proof systems whenever they're like studied academically and peer- reviewed are usually studied as interactive protocols but in practice zero knowledge proofs are used as non-interactive protocols which essentially means the prover uh produces a single proof with no interaction with the verifier and just sends them a single payload that they can verify and the thing that makes this possible is the Fiat Shamir heuristic and this is like a sort of very deep Topic in cryptography and uh it is often left as an implementation detail when these systems are studied academically and it famously can go wrong in a ton of ways and actually just recently there was a paper proving that um even very natural like seemingly correct uses of Fiat Shamir can be vulnerable and um even more recently this week I saw there was a paper talking about how to defend against taxs like attacks like this sort of very generally so this space is kind of developing very rapidly it's just something to be aware of and keep an eye on and of course the most common place for there to be bugs in a ZK application is in the application code itself so usually this is where you implement your logic that you're trying to constrain or prove incorrectly generally this means you're under constraining something like it's more common for you to under constrain something uh an example is like missing a range check on some value so a range check ensures that a certain variable isn't too large or isn't too small um and this is a very common type of constraint that people Miss sometimes you can over constrain variables an example would be like if there's some Edge case in what you're trying to prove and you forget to implement that in your circuits then you're unable to prove any data where that edge case occurs and another category are integration issues so this is where even if the zero knowledge proof itself is correct uh it requires you to write your application in a certain way so an example is in systems like tornado cache there's a value called the nullifier which essentially proves that you have made a deposit and the proof system itself doesn't check that you only prove that once it doesn't check that you only use this nullifier once and so that's up to your application code to sort of the thing wraid the zero knowledge proof system to check that this value is only used once and there are many ways for that to go wrong as well so I want to give a quick example of what a soundless bug can look like and how it can be exploited so this is a bug that um chain light found in zync Era an early version of ZK sync era um and basically it's an application layer bug but I view it as sort of more of the fault of a foot gun in the front end standard Library so there's something called a linear combination Gadget um and it was very easy to forget a call to a certain function that actually generates the constraints like you could construct the linear combination but forget to constrain it and this linear combination was used sort of to decompose a 256-bit value into two 128bit values and this code here creates a linear combination that's supposed to check that uh that decomposition is happening correctly but but they forgot to call the enforce zero so no constraints were actually generated which meant that the highest 128 bits that get written into memory in the zero knowledge uh VM were unconstrained and the pr could put whatever they want there so this is a very small bug it's like one small constraint that's missing but it actually has critical impact so um basically every smart contract on a layer two uses memory rights and on pync era there's something called uh system contracts which are privileged contracts that perform very sensitive uh operations necessary to the L2 an example would be withdrawing eth from the layer 2 to the layer one there's the system contract and basically it burns your eth on the L2 and generates this withdraw message that gets put in the block route that then you can finalize on layer one and this withdraw message gets written into memory and so a malicious would be able to manipulate the amount of eth that gets written in this withdraw message and then on layer one prove that invalid block and finalize the withdrawal that withdraws more eth than they were attempting to withdraw so it's just a oneline fix it's just to call this function that actually generates the constraints uh and we reported this vulnerability to ZK Sy era's bug Bounty program very very early on they were one of the only chains that had um a bug boundy program and especially for zero knowledge circuits so big kudos to them for that and uh we actually fully implemented the exploit for this to as kind of like a public service like to demonstrate how zero knowledge exploits can look and like hopefully educate people on the risks so um zky era at the time actually had a very crucial mitigation and this is another big Testament to their um like their efforts and security so they had something called an execution delay which means that there's a time lock after the block is proven before anything in it is considered finalized and what this means is that if they have good monitoring that can check uh if an anomalous block was proven they can intervene before any of those withdrawals or any other transactions are finalized so um one concern around this is that you have like the centralized governance or Security Council that can intervene um and you can actually do something really cool with zero knowledge proofs called self-cancellation so this is where if you can prove multiple independent block routes for the next block that implies that there's a soundness bug and so during this execution delay you can not allow any intervention unless you're able to prove two different blocks and only then do you allow intervention so it's this extra kind of security layer that reduces centralization risk from a security Council or governance another really important mitigation you can have are called multi proofs multi- proofs are kind of like multi sigs for wallets but with proof systems so the attacker has to compromise all of the proof systems at the same time meaning they have to prove the same invalid data say like for example the same invalid block route in all of the proof systems at once which dramatically reduces the risk of there being a bug so the ideal is that you have completely independent implementations of the full ZK stack so you write separate application code and use separate front ends and backends this is of course like a really big ask um because even having the same team Implement both is introducing some risk that they implement the same bug in all the systems uh so a slightly easier First Step that a lot of l2s are currently working on is using te at the stations as a second Factor so this doesn't require doing a full ZK proof but it does add some security to your proof system because a te attestation in theory should be difficult to uh to forge and especially difficult for an attacker to forge that while also breaking a ZK proof system in the exact same way uh it's worth noting there is a trade-off you're making here so if you require multi proofs it means you need to be able to prove the block with all of the proof systems at the same time uh which means you're risking if there's a completeness bug in any of the proof systems then you're unable to prove that block um in your multi-prover but generally soundness bugs have higher impact and are more critical than uh completeness bugs so this is generally a trade-off worth making for security and there's even sort of self-cancellation type things you can do where um you only uh basically like if there's a completeness bug you can also confirm that because the uh no proofs have been finalized for a long time and then you can allow an upgrade that fixes the completeness bug so I want to end with some general recommendations so if you're a developer writing a ZK app you should choose very battle tested front ends and back ends this means they should have multiple high quality Audits and IDE be in scope for a very large bug Bounty um another good thing to see is if there's formal verification of some of the components of the front end or back end um it's kind of infeasible still to do endtoend verification of a ZK application but you can do smaller things like formally verify that the verification logic like the smart contract that verifies the proof is correct uh of course you should get application layer audits uh zero knowledge bugs are very subtle and you know it's likely that one audit won't catch them all but audits are still the best way to find zero knowledge bugs if you look at any of the audit reports from ourselves or other good ZK audit firms they basically always find bugs or highlight important failure modes that are like crucial to be aware of um multipro so again the minimum is a ZK proof plus a te this significantly raises the bar for attackers the ideal is you have separate teams Implement your full ZK stack um independently and do multiple zero knowledge proofs as part of your multipro and yeah the last line of defense if possible for your application is to have some sort of finalization delay in monitoring where you can detect an anomalous proof has been submitted and do some sort of uh intervention yeah so uh I can take any questions you can DM me on Twitter or catch me uh off stage if you want to ask anything else thank you
Automatic transcript — names and jargon may be misspelled.