此前,Beosin 安全研究人員在 SnarkJS 0.6.11及之前的版本的庫中發現了一個嚴重漏洞,當該庫在驗證證明時未對參數進行完整的合法性檢查,使得攻擊者可以僞造出多個證明通過校驗,實現雙花攻擊。SnarkJS 是一款用於構建零知識證明的开源 JavaScript 庫,廣泛應用於 zk-SNARK 技術的實現和優化。
Beosin在提了這個漏洞以後,第一時間聯系項目方並協助修復,目前該漏洞已修復完成。Beosin提醒所有使用了SnarkJS庫的zk項目方可將SnarkJS更新到 0.7.0版本!以確保安全性。
現在,Beosin以長文的形式與大家分享漏洞原理,以及提醒項目方需要注意什么。
前言
Circom是基於Rust开發的零知識證明電路編譯器,該團隊同時开發了SnarkJS庫用於實現證明系統,包括:可信設置、零知識證明的生成和驗證等,支持Groth16、PLONK、FFLONK算法。
https://docs.circom.io/
2. 漏洞原理
Beosin安全實驗室漏洞研究人員在SnarkJS小於等於0.6.11版本中發現,當該庫在驗證證明時未對參數進行完整的合法性檢查,使得攻擊者可以僞造出多個證明通過校驗,實現雙花攻擊。數學依據:首先如果要在以太坊中生成和驗證zk-SNARK證明,需要使用 F_p-arithmetic 有限域橢圓曲线電路,其中曲线的一般方程如下:
可以發現曲线上的點都會進行一個模p運算,所以電路生成的證明參數s值取值範圍爲[0,1,…,p-1],那么當SnarkJS的變量範圍大於電路取值範圍時,存在下列多個具有相同輸出的證明參數值:
綜上,只要知道了其中一個合法的證明參數s,參數範圍內的s+np( n = 1,2,…,n)都可以滿足驗證計算,於是攻擊者在獲取到任意驗證通過的s,即可構造多個s可以通過校驗,具體的攻擊流程如下:
上文可知,參數的取值範圍由p決定,而不同類型的F_p對應不同的p,需要根據具體使用的零知識算法確定。
3. 漏洞實現
使用snarkjs庫進行鏈下驗證時,在groth16Verify函數中並未校驗publicSignals參數的取值範圍合法性,導致可以僞造證明通過校驗:
4. PoC
初始的originalHash驗證通過,接着使用剛僞造的attackHash同樣驗證通過!即同一份proof,可以被多次驗證通過,即可造成雙花攻擊。
此外,由於本文使用ALT_BN128 曲线進行復現,因此共計可以生成6個不同參數通過驗證:
4. 修復方案
Circom 項目已經針對該通用漏洞進行了修復,涉及到其實現的共計3個算法:
https://github.com/iden3/snarkjs/issues/358
1)groth16_verify.js
https://github.com/iden3/snarkjs/commit/7f462cc932191348a057e6e18f377154369104fc#
2)flonk_verify.js
3)plonk_verify.js
針對此漏洞,Beosin安全團隊提醒zk項目方,在進行proof驗證時,應充分考慮算法設計在實際實現時,由於代碼語言屬性導致的安全風險。同時,強烈建議項目方在項目上线之前,尋求專業的安全審計公司進行充分的安全審計,確保項目安全。
5. 漏洞後續
目前該漏洞已經被收錄到github advisory database中,且爲評分7.5的高危漏洞:
https://github.com/advisories/GHSA-xp5g-jhg3-3rg2
同時該高危漏洞也被更新到npm庫中,安裝舊版本snarkjs庫時會有如下預警信息:
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。