1. Background
Circom is a zero-knowledge proof circuit compiler developed in Rust. The team behind Circom has also developed the SnarkJS library, which is used to implement the proof system. SnarkJS supports various functionalities, including trusted setups, generation and verification of zero-knowledge proofs. It also provides support for Groth16, PLONK, and FFLONK algorithms.
2. Vulnerability Description
Beosin security researchers have discovered a vulnerability in versions of SnarkJS <= 0.6.11. In these versions, the library fails to perform comprehensive validation checks on the parameters during proof verification. This allows attackers to forge multiple proofs that pass the verification process, enabling double-spending attacks.
To generate and verify zk-SNARK proofs in Ethereum, F_p-arithmetic finite field elliptic curve circuits are used. The general equation for the curve is as follows:
It can be observed that points on the curve undergo a modulo p operation, so the proof parameter s generated by the circuit has a value range of [0,1,…,p-1]. When the variable range in SnarkJS exceeds the value range of the circuit, multiple proof parameter values with the same output can exist.
In summary, if one valid proof parameter s is known, any s within the parameter range s + np (where n = 1,2,…,n) can satisfy the verification calculation. Therefore, once an attacker obtains any s that passes the verification, they can construct multiple s values that pass the validation. The specific attack process is as follows:
As can be seen above, the range of values of the parameters is determined by p, while different types of F_p correspond to different p and need to be determined according to the specific zero-knowledge algorithm used.
3. Vulnerability Implementation
When using the snarkjs library for off-chain verification, the groth16Verify function does not validate the legality of the publicSignals parameter’s value range. This vulnerability allows for the forging of proofs that pass the verification process.
4. PoC
The initial originalHash is validated, and then the attackHash just forged is also validated. That is, the same proof can be verified more than once, resulting in a double-spending attack.
In addition, since the ALT_BN128 curve was used for reproduction, a total of six different parameters could be generated for validation:
5. Remediation
Circom has fixed this general vulnerability for a total of three algorithms involved in its implementation.
https://github.com/iden3/snarkjs/issues/358
1)groth16_verify.js
(Add check functions in js and sc · iden3/snarkjs@7f462cc · GitHub
2)flonk_verify.js
3)plonk_verify.js
In response to this vulnerability, Beosin security team reminds zk projects to fully consider the security risks caused by the code language properties of the algorithm design during the actual implementation when proof verification is performed. It is also strongly recommended that the project seek a professional security audit company to conduct a full security audit before going live.
6. Follow-up
The high risk vulnerability has been included in the github advisory database and with a rating of 7.5.
(https://github.com/advisories/GHSA-xp5g-jhg3-3rg2)
The high-risk vulnerability has also been updated to the npm library, and the following warning message will be displayed when installing older versions of the snarkjs library.
7. Best Practice
Since the maximum range of the finite domain of elliptic curve algorithms in zero-knowledge proof circuits is smaller compared to the maximum value that can be expressed by the data type in the system implementation, it is possible to forge multiple proofs, leading to double-spending attacks. This issue is not limited to the algorithms used in proof systems such as Groth16, PLONK, or FFLONK but is a general issue. It is advisable to address and prevent such problems during the implementation of zk-proof systems.
Contact
If you need any blockchain security services, welcome to contact us: