您的位置:首頁(yè) > 區(qū)塊鏈 >

zkSNARKs可能有雙花漏洞 可修改證明繞過防雙花檢

2019-07-30 11:46:38 來源: 巴比特

大量零知識(shí)證明項(xiàng)目由于錯(cuò)誤地使用了某個(gè) zkSNARKs 合約庫(kù),引入「輸入假名 (Input Aliasing) 」漏洞,可導(dǎo)致偽造證明、雙花、重放等攻擊行為

大量零知識(shí)證明項(xiàng)目由于錯(cuò)誤地使用了某個(gè) zkSNARKs 合約庫(kù),引入「輸入假名 (Input Aliasing) 」漏洞,可導(dǎo)致偽造證明、雙花、重放等攻擊行為發(fā)生,且攻擊成本極低。眾多以太坊社區(qū)開源項(xiàng)目受影響,其中包括三大最常用的 zkSNARKs 零知開發(fā)庫(kù) snarkjs、ethsnarks、ZoKrates,以及近期大熱的三個(gè)混幣(匿名轉(zhuǎn)賬)應(yīng)用 hopper、Heiswap、Miximus。這是一場(chǎng)由 Solidity 語(yǔ)言之父 Chris 兩年前隨手貼的一段代碼而引發(fā)的血案。

雙花漏洞:最初暴露的問題

semaphore 是一個(gè)使用零知識(shí)證明技術(shù)的匿名信號(hào)系統(tǒng),該項(xiàng)目由著名開發(fā)者 barryWhiteHat 此前的混幣項(xiàng)目演化而來。

俄羅斯開發(fā)者 poma 最先指出該項(xiàng)目可能存在雙花漏洞[1]。

問題出在第 83 行代碼[2],請(qǐng)仔細(xì)看。

該函數(shù)需要調(diào)用者構(gòu)造一個(gè)零知識(shí)證明,證明自己可從合約中提走錢。為了防止「雙花」發(fā)生,該函數(shù)還讀取「廢棄列表」,檢查該證明的一個(gè)指定元素是否被標(biāo)記過。如果該證明在廢棄列表中,則合約判定校驗(yàn)不通過,調(diào)用者無(wú)法提走錢。開發(fā)者認(rèn)為,這樣一來相同的證明就無(wú)法被重復(fù)提交獲利,認(rèn)為此舉可以有效防范雙花或重放攻擊。

然而事與愿違,這里忽視了一個(gè)致命問題。攻擊者可根據(jù)已成功提交的證明,利用「輸入假名」漏洞,對(duì)原輸入稍加修改便能迅速「?jìng)卧熳C明」,順利通過合約第 82 行的零知識(shí)證明校驗(yàn),并繞過第 83 行的防雙花檢查。

該問題最早可追溯到 2017 年,由 Christian Reitwiessner 大神,也就是 Solidity 語(yǔ)言的發(fā)明者,提供的 zkSNARKs 合約密碼學(xué)實(shí)現(xiàn)示例[3]。其后,幾乎以太坊上所有使用 zkSNARKs 技術(shù)的合約,都照用了該實(shí)現(xiàn)。因此都可能遭受以下流程的攻擊。

混幣應(yīng)用:該安全問題的重災(zāi)區(qū)

零知識(shí)證明技術(shù)在以太坊上最早和最廣泛的應(yīng)用場(chǎng)景是混幣合約,或匿名轉(zhuǎn)賬、隱私交易。由于以太坊本身不支持匿名交易,而社區(qū)對(duì)于隱私保護(hù)的呼聲越來越強(qiáng)烈,因此涌現(xiàn)出不少熱門項(xiàng)目。這里以混幣合約的應(yīng)用場(chǎng)景為例,介紹「輸入假名」漏洞對(duì)零知項(xiàng)目的安全威脅。

混幣合約或匿名轉(zhuǎn)賬涉及兩個(gè)要點(diǎn):

證明自己有一筆錢

證明這筆錢沒有花過

為了方便理解,這里簡(jiǎn)單描述一下流程:

A 要花一筆錢。

A 要證明自己擁有這筆錢。A 出示一個(gè) zkproof,證明自己知道一個(gè) hash (HashA) 的 preimage,且這個(gè) hash 在以 root 為標(biāo)志的 tree 的葉子上,且證明這個(gè) preimage 的另一種 hash 是 HashB。其中 HashA 是 witness,HashB 是 public statement。由于 A 無(wú)需暴露 HashA,所以是匿名的。

合約校驗(yàn) zkproof,并檢查 HashB 是否在廢棄列表中。若不在,則意味著這筆錢未花過,可以花(允許 A 的此次調(diào)用)。

如果可以花,合約需要把 HashB 放入廢棄列表中,標(biāo)明以 HashB 為代表的錢已經(jīng)被花過,不能再次花了。

上面代碼中的第 82 行 verifyProof(a, b, c, input) 用來證明這筆錢的合法性,input[] 是 public statement,即公共參數(shù)。第 83 行通過 require(nullifiers_set[input[1]] == false) 校驗(yàn)這筆錢是否被花過。

很多 zkSNARKs 合約尤其是混幣合約,核心邏輯都與第 82 行和 83 行類似,因此都存在同樣的安全問題,可利用「輸入假名」漏洞進(jìn)行攻擊。

漏洞解析:一筆錢如何匿名地重復(fù)花 5 次?

上面 verifyProof(a, b, c, input) 函數(shù)的作用是根據(jù)傳入的數(shù)值在橢圓曲線上進(jìn)行計(jì)算校驗(yàn),核心用到了名為 scalar_mul() 的函數(shù),實(shí)現(xiàn)了橢圓曲線上的標(biāo)量乘法[4]。

/// @return the product of a point on G1 and a scalar, i.e.

/// p == p.scalar_mul(1) and p.add(p) == p.scalar_mul(2) for all points p.

function scalar_mul(G1Point point, uint s) internal returns (G1Point r) {

uint[3] memory input;

input[0] = p.X;

input[1] = p.Y;

input[2] = s;

bool success;

assembly {

success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60)

// Use "invalid" to make gas estimation work

switch success case 0 { invalid() }

}

require (success);

}

我們知道以太坊內(nèi)置了多個(gè)預(yù)編譯合約,進(jìn)行橢圓曲線上的密碼學(xué)運(yùn)算,降低 zkSNARKs 驗(yàn)證在鏈上的 Gas 消耗。函數(shù) scalar_mul() 的實(shí)現(xiàn)則調(diào)用了以太坊預(yù)編譯 7 號(hào)合約,根據(jù)EIP 196實(shí)現(xiàn)了橢圓曲線 alt_bn128 上的標(biāo)量乘法[5]。下圖為黃皮書中對(duì)該操作的定義,我們常稱之為 ECMUL 或 ecc_mul。

密碼學(xué)中,橢圓曲線的 {x,y} 的值域是一個(gè)基于 mod p 的有限域,這個(gè)有限域稱之為 Zp 或 Fp。也就是說,一個(gè)橢圓曲線上的一個(gè)點(diǎn) {x,y} 中的 x,y 是 Fp 中的值。一條橢圓曲線上的某些點(diǎn)構(gòu)成一個(gè)較大的循環(huán)群,這些點(diǎn)的個(gè)數(shù)稱之為群的階,記為q?;跈E圓曲線的加密就在這個(gè)循環(huán)群中進(jìn)行。如果這個(gè)循環(huán)群的階數(shù)(q)為質(zhì)數(shù),那么加密就可以在 mod q 的有限域中進(jìn)行,該有限域記作 Fq。

一般選取較大的循環(huán)群作為加密計(jì)算的基礎(chǔ)。在循環(huán)群中,任意選定一個(gè)非無(wú)窮遠(yuǎn)點(diǎn)作為生成元 G(通常這個(gè)群的階q是個(gè)大質(zhì)數(shù),那么任選一個(gè)非零點(diǎn)都是等價(jià)的),其他所有的點(diǎn)都可以通過 G+G+.... 產(chǎn)生出來。這個(gè)群里的元素個(gè)數(shù)為 q,也即一共有 q 個(gè)點(diǎn),那么我們可以用 0,1,2,3,....q-1 來編號(hào)每一個(gè)點(diǎn)。在這里第 0 個(gè)點(diǎn)是無(wú)窮遠(yuǎn)點(diǎn),點(diǎn)1 就是剛才提到的那個(gè) G,也叫做基點(diǎn)。點(diǎn)2 就是 G+G,點(diǎn)3 就是 G+G+G。

于是當(dāng)要表示一個(gè)點(diǎn)的時(shí)候,我們有兩種方式。第一種是給出這個(gè)點(diǎn)的坐標(biāo) {x,y},這里 x,y 屬于Fp。第二種方式是用 n*G 的方式給出,由于 G 是公開的,于是只要給出 n 就行了。n 屬于 Fq。

看一下 scalar_mul(G1Point point, uint s) 函數(shù)簽名,以 point 為生成元,計(jì)算 point+point+.....+point,一共 n 個(gè) point 相加。這屬于使用上面第二種方法表示循環(huán)群中的一個(gè)點(diǎn)。

在 Solidity 智能合約實(shí)現(xiàn)中需要使用 uint256 類型來編碼 Fq,但 uint256 類型的最大值是大于q 值,那么 會(huì)出現(xiàn)這樣一種情況:在 uint256 中有多個(gè)數(shù) 經(jīng)過 mod 運(yùn)算之后都會(huì)對(duì)應(yīng)到同一個(gè) Fq中的值。比如 s 和 s + q 表示的其實(shí)是同一個(gè)點(diǎn),即第s個(gè)點(diǎn)。這是因?yàn)樵谘h(huán)群中點(diǎn)q 其實(shí)等價(jià)于 點(diǎn)0(每個(gè)點(diǎn)分別對(duì)應(yīng) 0,1,2,3,....q-1)。同理,s + 2q 等均對(duì)應(yīng)到點(diǎn)s 。我們把可以輸入多個(gè)大整數(shù)會(huì)對(duì)應(yīng)到同一個(gè) Fq中的值 這一現(xiàn)象稱作「輸入假名」,即這些數(shù)互為假名。

以太坊 7 號(hào)合約實(shí)現(xiàn)的橢圓曲線是 y^2 = ax^3+bx+c。p 和 q 分別如下。

這里的 q 值即上文中提到的群的階數(shù)。那么在 uint256 類型范圍內(nèi),共有 uint256_max / q 個(gè),算下來也就是最多會(huì)有 5 個(gè)整數(shù)代表同一個(gè)點(diǎn)( 5 個(gè)「輸入假名」)。

這意味著什么呢?讓我們回顧上面調(diào)用 scalar_mul(G1Point point, uint s) 的 verifyProof(a, b, c, input) 函數(shù),input[] 數(shù)組里的每個(gè)元素實(shí)際就是 s。對(duì)于每個(gè) s,在 uint256 數(shù)據(jù)類型范圍內(nèi),會(huì)最多存在其他 4 個(gè)值,傳入后計(jì)算結(jié)果與原值一致。

因此,當(dāng)用戶向合約出示零知識(shí)證明進(jìn)行提現(xiàn)后,合約會(huì)把 input[1] (也就是某個(gè) s)放入作廢列表。用戶(或其他攻擊者)還可以使用另外 4 個(gè)值再次進(jìn)行證明提交。而這 4 個(gè)值之前并沒有被列入「廢棄列表」,因此“偽造”的證明可以順利通過校驗(yàn),利用 5 個(gè)「輸入假名」一筆錢可以被重復(fù)花 5 次,而且攻擊成本非常低!

還有更多受影響的項(xiàng)目

存在問題的遠(yuǎn)遠(yuǎn)不止 semaphore 一個(gè)。其他很多以太坊混幣項(xiàng)目以及 zkSNARKs 項(xiàng)目都存在同樣的允許「輸入假名」的問題。

這些項(xiàng)目在社區(qū)熱度都十分高,其中 Heiswap 更是被人們稱為 「Vitalik 最喜愛的項(xiàng)目」。

這當(dāng)中,影響最大的要數(shù)幾個(gè)大名鼎鼎的 zkSNARKs 庫(kù)或框架項(xiàng)目,包括 snarkjs、ethsnarks、ZoKrates 等。許多應(yīng)用項(xiàng)目會(huì)直接引用或參考他們的代碼進(jìn)行開發(fā),從而埋下安全隱患。因此,上述三個(gè)項(xiàng)目迅速進(jìn)行了安全修復(fù)更新。另外,多個(gè)利用了 zkSNARKs 技術(shù)的知名混幣項(xiàng)目,如 hopper、Heiswap、Miximus 也立刻進(jìn)行了同步修復(fù)。

「輸入假名」漏洞的解決方案

事實(shí)上,所有使用了該 zkSNARKs 密碼學(xué)合約庫(kù)的項(xiàng)目都應(yīng)該立即開展自查,評(píng)估是否受影響。那么應(yīng)該如何修復(fù)這個(gè)問題?

所幸的是,修復(fù)很簡(jiǎn)單。僅需在驗(yàn)證函數(shù)中添加對(duì)輸入?yún)?shù)大小的校驗(yàn),強(qiáng)制要求 input 值小于上面提到的 q 值。即嚴(yán)禁「輸入假名」,杜絕使用多個(gè)數(shù)表示同一個(gè)點(diǎn)。

暴露的深層問題值得反思

該「輸入假名」導(dǎo)致的安全漏洞值得社區(qū)認(rèn)真反思。我們?cè)倩仡櫼幌抡麄€(gè)故事。2017 年 Christian 在 Gist 網(wǎng)站貼出了自己的 zkSNARKs 合約計(jì)算實(shí)現(xiàn)。作為計(jì)算庫(kù),我們可以認(rèn)為他的實(shí)現(xiàn)并沒有安全問題,沒有違反任何密碼學(xué)常識(shí),完美地完成了在合約中進(jìn)行證明驗(yàn)證的工作。

事實(shí)上,作為 Solidity 語(yǔ)言的發(fā)明者,Christian 在這里當(dāng)然不會(huì)犯任何低級(jí)錯(cuò)誤。而兩年后的今天,這段代碼卻引發(fā)了如此的安全風(fēng)波。兩年多的時(shí)間內(nèi),可能有無(wú)數(shù)同行和專家看過或使用過這段只有兩百多行的代碼,卻沒有發(fā)現(xiàn)任何問題。

核心問題出在哪里?可能出在底層庫(kù)的實(shí)現(xiàn)者和庫(kù)的使用者雙方間對(duì)于程序接口的理解出現(xiàn)了偏差。換句話說:底層庫(kù)的實(shí)現(xiàn)者對(duì)于應(yīng)用開發(fā)者的不當(dāng)使用方式欠缺考慮;而上層應(yīng)用開發(fā)者沒有在使用中沒有深入理解底層實(shí)現(xiàn)原理和注意事項(xiàng),進(jìn)行了錯(cuò)誤的安全假設(shè)。

所幸的是,目前常見的 zkSNARKs 合約庫(kù)都火速進(jìn)行了更新,從底層庫(kù)層面杜絕「輸入假名」。安比(SECBIT)實(shí)驗(yàn)室認(rèn)為,底層庫(kù)的更新誠(chéng)然能夠很大程度上消除掉后續(xù)使用者的安全隱患,但若該問題的嚴(yán)重性沒有得到廣泛地宣傳和傳播,依舊會(huì)有開發(fā)者不幸使用到錯(cuò)誤版本的代碼,或者是根據(jù)錯(cuò)誤的教程進(jìn)行開發(fā)(就像因?yàn)檎麛?shù)溢出而歸零的那些 Token 一樣),從而埋下安全隱患。

「輸入假名」漏洞不禁讓我們回想起此前頻繁曝出的「整數(shù)溢出」漏洞。二者相似之處頗多:都是源于大量開發(fā)者的錯(cuò)誤假設(shè);都與 Solidity 里的 uint256 類型有關(guān);波及面都十分廣;網(wǎng)絡(luò)上也都流傳著很多存在隱患的教程代碼或者庫(kù)合約。

但顯然「輸入假名」漏洞顯然更難檢測(cè),潛伏時(shí)間更長(zhǎng),需要的背景知識(shí)更多(涉及到復(fù)雜的橢圓曲線和密碼學(xué)理論)。安比(SECBIT)實(shí)驗(yàn)室認(rèn)為,隨著 zkSNARKs、零知識(shí)證明應(yīng)用、隱私技術(shù)的興起,社區(qū)會(huì)涌現(xiàn)出更多的新應(yīng)用,而背后暗藏的更多安全威脅可能會(huì)進(jìn)一步暴露出來。希望這波新技術(shù)浪潮中,社區(qū)能充分吸收以往的慘痛教訓(xùn),重視安全問題。(作者:p0n1)

關(guān)鍵詞: zkSNARKs 雙花漏洞 證明

精選 導(dǎo)讀

募資55億港元萬(wàn)物云啟動(dòng)招股 預(yù)計(jì)9月29日登陸港交所主板

萬(wàn)科9月19日早間公告,萬(wàn)物云當(dāng)日啟動(dòng)招股,預(yù)計(jì)發(fā)行價(jià)介乎每股47 1港元至52 7港元,預(yù)計(jì)9月29日登陸港交所主板。按發(fā)行1 167億股計(jì)算,萬(wàn)

發(fā)布時(shí)間: 2022-09-20 10:39
管理   2022-09-20

公募基金二季度持股情況曝光 隱形重倉(cāng)股多為高端制造業(yè)

隨著半年報(bào)披露收官,公募基金二季度持股情況曝光。截至今年二季度末,公募基金全市場(chǎng)基金總數(shù)為9794只,資產(chǎn)凈值為269454 75億元,同比上

發(fā)布時(shí)間: 2022-09-02 10:45
資訊   2022-09-02

又有上市公司宣布變賣房產(chǎn) 上市公司粉飾財(cái)報(bào)動(dòng)作不斷

再有上市公司宣布變賣房產(chǎn)。四川長(zhǎng)虹25日稱,擬以1 66億元的轉(zhuǎn)讓底價(jià)掛牌出售31套房產(chǎn)。今年以來,A股公司出售房產(chǎn)不斷。根據(jù)記者不完全統(tǒng)

發(fā)布時(shí)間: 2022-08-26 09:44
資訊   2022-08-26

16天12連板大港股份回復(fù)深交所關(guān)注函 股份繼續(xù)沖高

回復(fù)交易所關(guān)注函后,大港股份繼續(xù)沖高。8月11日大港股份高開,隨后震蕩走高,接近收盤時(shí)觸及漲停,報(bào)20 2元 股。值得一提的是,在7月21日

發(fā)布時(shí)間: 2022-08-12 09:56
資訊   2022-08-12

萬(wàn)家基金再添第二大股東 中泰證券擬受讓11%基金股權(quán)

7月13日,中泰證券發(fā)布公告,擬受讓齊河眾鑫投資有限公司(以下簡(jiǎn)稱齊河眾鑫)所持有的萬(wàn)家基金11%的股權(quán),交易雙方共同確定本次交易的標(biāo)的資

發(fā)布時(shí)間: 2022-07-14 09:39
管理   2022-07-14

央行連續(xù)7日每天30億元逆回購(gòu) 對(duì)債市影響如何?

央行12日再次開展了30億元逆回購(gòu)操作,中標(biāo)利率2 10%。這已是央行連續(xù)7日每天僅進(jìn)行30億元的逆回購(gòu)縮量投放,創(chuàng)下去年1月以來的最低操作規(guī)

發(fā)布時(shí)間: 2022-07-13 09:38
資訊   2022-07-13

美元指數(shù)創(chuàng)近20年新高 黃金期貨創(chuàng)出逾9個(gè)月新低

由于對(duì)美聯(lián)儲(chǔ)激進(jìn)加息的擔(dān)憂,美元指數(shù)11日大漲近1%創(chuàng)出近20年新高。受此影響,歐美股市、大宗商品均走弱,而黃金期貨創(chuàng)出逾9個(gè)月新低。美

發(fā)布時(shí)間: 2022-07-13 09:36
資訊   2022-07-13

美股三大股指全線下跌 納斯達(dá)克跌幅創(chuàng)下記錄以來最大跌幅

今年上半年,美股持續(xù)回落。數(shù)據(jù)顯示,道瓊斯指數(shù)上半年下跌15 3%,納斯達(dá)克綜合指數(shù)下跌29 5%,標(biāo)普500指數(shù)下跌20 6%。其中,納斯達(dá)克連續(xù)

發(fā)布時(shí)間: 2022-07-04 09:51
推薦   2022-07-04

融資客熱情回升 兩市融資余額月內(nèi)增加超344億元

近期A股走強(qiáng),滬指6月以來上漲4%,融資客熱情明顯回升。數(shù)據(jù)顯示,截至6月16日,兩市融資余額1 479萬(wàn)億元,月內(nèi)增加344 67億元,最近一個(gè)半

發(fā)布時(shí)間: 2022-06-20 09:41
資訊   2022-06-20

4個(gè)交易日凈買入超百億元 北向資金持續(xù)流入A股市場(chǎng)

北向資金凈流入態(tài)勢(shì)延續(xù)。繼6月15日凈買入133 59億元后,北向資金6月16日凈買入44 52億元。自5月27日至今,除6月13日以外,北向資金累計(jì)凈

發(fā)布時(shí)間: 2022-06-17 09:37
推薦   2022-06-17

熱門TAG

more
美聯(lián)儲(chǔ)今年已將基準(zhǔn)利率從接近零大幅上調(diào)至略高于3% EIA報(bào)告:美國(guó)原油庫(kù)存及戰(zhàn)略儲(chǔ)備減少,汽油及精煉油庫(kù)存輕微波動(dòng) 美國(guó)政府更廣泛推動(dòng)從汽油動(dòng)力汽車轉(zhuǎn)向電動(dòng)汽車的一部分 數(shù)據(jù)顯示:今年9月日本船企接單量延續(xù)8月下跌下跌趨勢(shì) 公告顯示:2022年前三季度TCL中環(huán)研發(fā)投入為27億元 占比營(yíng)業(yè)收入5.42% 新的111.75億英鎊注資列在“對(duì)金融機(jī)構(gòu)的援助—支付給英格蘭銀行”標(biāo)題下 本次政府儲(chǔ)備肉投放面向北京18家主要連鎖超市門店及相關(guān)零售終端投放 有交易員預(yù)計(jì):如果LME不采取措施 接下來可能將有數(shù)十萬(wàn)噸鋁流入LME 據(jù)報(bào)道:繼德國(guó)最大釀酒商拉德貝格啤酒公司9月宣布漲價(jià) 據(jù)報(bào)道:澳大利亞礦商Pilbara的鋰礦拍賣價(jià)再創(chuàng)新高 折算后的碳酸鋰成本 中集天達(dá)首次公開發(fā)行A股股票 招股書顯示此次擬公開發(fā)行股數(shù)不超過103, 多家銀行加強(qiáng)綠色金融頂層設(shè)計(jì) 致力于為經(jīng)濟(jì)社會(huì)綠色低碳轉(zhuǎn)型貢獻(xiàn)力量 萊特幣 比特幣 數(shù)字資產(chǎn) 火幣 以太經(jīng)典 比特股 EOS 比特幣現(xiàn)金 量子鏈 Hcash 泰達(dá)幣 瑞波幣 Qcash 比特幣鉆石 超級(jí)比特幣 優(yōu)幣 硬分叉 加密貨幣