賈瑤琪:攻擊無處不在,區塊鏈安全和隱私問題有點與眾不同

買賣虛擬貨幣

大家好,我是瑤琪,Parity亞洲技術總監。感謝萬向區塊鏈的邀請,有機會跟大家分享區塊鏈相關的安全和隱私問題。我目前在Parity負責Web3.0在亞洲,特別是國內的技術和社羣發展。Parity之前主要做以太坊Rust客戶端,目前主要開發區塊鏈框架Substrate和跨鏈網路Polkadot。

今天我將分享一些過去幾年區塊鏈遇到的攻擊例項,為大家展示一下不太一樣的區塊鏈安全和隱私問題。

第一,惡意攻擊。這些攻擊大多是大家平時接觸到的區塊鏈相關的智慧合約攻擊。

第二,理性攻擊。平時可能比較少接觸到,也就是攻擊者如何利用遊戲規則之內的一些方法將自己的利益最大化,具體會以礦池和礦工為例。

第三,隱私攻擊。會根據針對Monero和Zcash的攻擊簡單介紹一下目前隱私區塊鏈系統存在的問題。

在過去幾年中,從比特幣到以太坊,區塊鏈系統從最初的分散式賬本功能,慢慢進化到現在類似於分散式計算機。初期的比特幣大家只能用於線上的電子支付或者跨境支付,但是現在有了各種各樣的智慧合約以及鏈上的執行邏輯,大家可以輕鬆的在以太坊以及其他區塊鏈平臺上進行程式設計和運算。

如上圖,大家可以看到不同的節點目前其實是蘊含對智慧合約等其他程式的處理能力。左邊的使用者可以使用金鑰進行資料簽名,然後傳送交易給節點,節點處理這些交易之後會更新對應的狀態生成區塊廣播給其他節點,其他節點會進行運算和驗證然後寫入本地的區塊鏈。這樣的分散式系統環環相扣,保證了不同國家、地區的使用者可以在這臺世界計算機上面進行操作和運算。

功能這麼強大的分散式計算機聽起來很酷,但是從安全形度來看,當一個系統支援的邏輯功能越多,其實它的安全隱患是越多的,這也是為什麼比特幣系統的安全性相對以太坊要好很多的一個原因。

參見以太坊的重大安全事件和,大家可以發現,如果一個區塊鏈平臺上的安全問題頻頻曝出,也會打擊大量的開發者以及一些創業公司在平臺上部署商業應用的信心。

當我們去看另一個區塊鏈平臺EOS,明顯可以感覺到對應的安全事件,其實是跟對應的價值關聯度更高。不管是鏈本身還是智慧合約層出不窮的安全問題,整體使得大家更加傾向於選擇去中心化程度高,以及更加安全的區塊鏈平臺去部署自己的商業邏輯。

過去一年有各種各樣的DeFi攻擊事件,其實也為大量的區塊鏈開發者敲醒了警鐘,當上線各種新功能之前一定要做好安全審計,儘量保證自己上線的功能是沒有較大的安全問題。

惡意攻擊

接下來我們看一下惡意攻擊,特別是與智慧合約相關的攻擊。

重入攻擊

2016年的DAO問題以及最近的DeFi問題,都是類似的攻擊。攻擊者編寫了對應的惡意智慧合約,呼叫受害者的合約,同時利用自己的回撥函式,迴圈地呼叫受害者合約的程式碼。由於是重複進入受害者合約執行對應的一段程式碼導致漏洞,所以把它叫做“重入攻擊”。

如上圖,右邊是惡意智慧合約,左邊是正常的智慧合約。正常的智慧合約如果按照左邊的邏輯,其實應該從上到下進行withdrawBalance,然後以太坊轉賬,最後更新對應使用者的餘額。

然而,惡意合約的執行邏輯則是,首先發起Withdrawal的函式,呼叫withdrawBalance函式,之後左邊正常的智慧合約會呼叫對應的withdrawBalance,以太坊轉賬,當轉賬一旦完成,由於以太坊本身的特性,就會呼叫惡意智慧合約裡面的回撥函式,回撥函式就會進一步的呼叫左邊的withdrawBalance。

整體來看,按照這個箭頭一二三四,形成了一個迴圈。導致的後果就是惡意合約在發起呼叫之後,可以一直不斷、如此往復地進行轉賬,把左邊智慧合約裡的餘額源源不斷地轉到攻擊者的地址,只要左邊智慧合約的燃料費是足夠的以及還有餘額,最終會把所有餘額轉給對應的攻擊者。

這是一個簡略的流程圖,大家可以看一下實際程式碼,左邊是正常的智慧合約,右邊是攻擊者的智慧合約。攻擊者智慧合約,在右上角進行發起,呼叫左邊withdrawBalance函式,之後呼叫call.value(),call.value()就觸發了惡意智慧合約的預設payable的回撥函式,預設回撥函式會進一步的再觸發迴圈,直至整個左邊智慧合約所有餘額都轉給惡意攻擊者。

由於重入攻擊,The DAO的合約在當時損失了超過6000萬美金的ETH。區塊鏈有不可篡改的特性,這樣的交易是不能回滾的,造成的損失是永久的。當時社羣有想修復這樣一個漏洞來退回對應損失的以太,也有一些社羣成員是不想的,最終導致了以太坊當時的分叉,分叉出了現在大家熟知的ETC/以太經典。

前事不忘後事之師,然而很多時候大家都會遺忘歷史,即使是跟安全息息相關的。最近的DeFi或者LendF.me的攻擊中,又見到了類似2016年的重入攻擊。由於過程其實比之前2016年攻擊要複雜很多,這裡就簡化說明一下。

Lendf.me押金函式被回撥函式中的withdraw反覆呼叫,導致了智慧合約中的攻擊者抵押的總額是在沒有足夠抵押金的情況下持續上升的。相當於透過一直呼叫這樣的迴圈,讓攻擊者本身的抵押總額一直上升,即使他沒有對應的抵押物,最終導致攻擊者積攢了足夠多imBTC對應的抵押金額。之後攻擊者借出所有可用的資產,超過2500萬美金。當然,比較幸運的是透過一些方法攻擊者返回對應的資產,也是不幸中的萬幸。

重入攻擊是很嚴重,防禦措施是怎樣呢?其實在做轉賬之前可以先把內部的狀態設定好,例如當智慧合約要給某個使用者轉10個ETH,在轉賬之前提前把使用者對應的餘額變數,減掉對應的10個ETH再做互動轉賬,就可以避免這樣的攻擊了。這就是一個比較好的規範,先去檢查,然後執行,最後再做互動。

溢位攻擊

另一個想給大家分享的攻擊也是過去幾年中比較普遍出現的攻擊,叫做“溢位攻擊”。大家如果是程式設計師都知道,不管是用C++還是JavaScript,當開發者操作算術運算不當就會出現越界,特別是整數溢位。當超出整數型別的最大範圍的時候,數字會由極大值變為極小值或者直接歸零,叫做“上溢”;相反的超出最小值範圍叫做“下溢”。

上圖是一個樣例。可以看到攻擊者發了好多交易,其中_value的對應值特別大。具體到257行,當_value特別大的時候,cnt如果大於2,amount就直接向上溢位為零,導致之後的安全檢查全部透過。259行的第二個檢查正常來說是不應該透過的,但是由於溢位的amount變成0了,左邊balances[msg.sender]的數值肯定大於等於零,導致第二部分的安全檢查也透過了。最終攻擊者成功獲取超大數目的數字資產,即使他本身的餘額可能會極其小。

其他的一些例子大家可以檢視multiOverflow (CVE-2018-10706),transferFlaw (CVE-2018–10468), proxyOverflow (CVE-2018-10376)。攻擊者通常都是利用智慧合約的溢位漏洞,製作出可以造成溢位的交易,之後透過溢位的變數來透過智慧合約的安全檢查,最終獲取巨大的資產。作為防禦措施,開發者儘量使用保證計算安全的庫,比如SafeMath。

所有權攻擊

所有權攻擊是指智慧合約的函式的所有權被攻擊者篡改。在一些攻擊中,有些函式設定了只有Owner才能看的訪問控制。然而,誰可以作為Owner呢?這個函式沒有實施訪問控制,任何人可以傳送交易給智慧合約把自己設定成Owner,然後就可以操縱其他函式。

還有一個例子是任何人可以透過呼叫公開函式,然後將自己的地址設定為CEO的地址,一旦你的地址變為CEO地址,你就可以進行CEO對應許可權的操作,也就可以有很高的許可權進行任何操作了。所有權攻擊利用了程式開發時訪問控制設計的漏洞,進行對應的攻擊。

擁塞攻擊

除了利用智慧合約漏洞的攻擊外,還有些攻擊其實是利用區塊鏈本身的特性進行攻擊。其中最有意思的一類攻擊叫做“擁塞攻擊”。比如Fomo 3D,前幾年Fomo 3D是很流行的彩票遊戲,最終獲得鑰匙的人可以獲得該合約的幾乎所有ETH。遊戲規則是怎樣的?在每一輪限定時間內大家都可以買鑰匙,每輪鑰匙價格會持續增高。如果在限定時間內有使用者參與買鑰匙,那麼下一輪就會繼續進行。直至當前輪在限定時間內沒有人買鑰匙,那麼上輪的買到鑰匙的使用者就是最終贏家。

Fomo3D的第一輪贏家也是攻擊者,他獲得了超過1萬以太的獎勵。正常情況下,遊戲規則是挺公平的,最後一輪使用者獲得最終獎勵。然而,攻擊者發現了以太坊本身的擁塞問題,透過自己的操作獲得了更高的成功空間,讓自己變成了最終的贏家。

這是以太坊什麼樣的漏洞呢?其實這個也不算以太坊的漏洞,是它的特性。

首先,以太坊本身的吞吐量不是特別高,每秒只能處理10到20個交易,當然現在相對高一點,未來以太坊2.0可能會更高。

然後,每一個區塊都有對應的燃料限制,為了保證以太坊的節點不受拒絕服務攻擊。贏家、攻擊者利用了這兩個特性:吞吐量低、有燃料限制。他製作了大量高手續費和可以消耗盡燃料的交易,使得其他對手交易不能被智慧合約處理,最終只處理自己超高手續費的交易,保證自己在最後一輪拿到鑰匙,接下來一輪別人的交易進不去,在限定時間內他就變成了最終贏家。

攻擊者在自己拿到鑰匙以後就傳送大量消費燃料和高手續費的交易,導致接下來的區塊只處理很少的交易。超高的手續費使得贏家可以讓自己的交易被節點進行打包,當節點只處理超高手續費交易的時候,燃料也耗盡了,其他交易進不來了。最終攻擊者就變成了最後一輪獲勝的贏家。

如果增加平臺吞吐量會不會有改進?攻擊者會不會完全沒辦法進行攻擊?其實不然。大家可以看另一個區塊鏈平臺EOS,EOS上有一款EOSPlay遊戲。這款遊戲利用未來區塊的雜湊值進行隨機數計算。通常情況下使用者是不清楚未來有哪些交易放入區塊鏈的,然而在擁塞的情況下,攻擊者是可以操作未來區塊包含的交易,提前計算出隨機數。實際攻擊中,攻擊者在CPU價格很低的時候買入大量的CPU資源,生成大量交易,讓對應的智慧合約處理交易。

由於攻擊者購買大量運算資源,導致當時的EOS平臺有比較大的擁塞,最終導致智慧合約和節點在進行運算時只有攻擊者交易是被寫到對應的區塊裡的。這些區塊裡的交易由於是攻擊者製作的,攻擊者可以進行簡單的排列組合,然後對排列組合裡交易進行計算,提前算出可能出現的隨機數,之後獲得遠超於其他正常使用者的優勢。最終攻擊者花費了大約21萬EOS,但是獲得了超過24萬EOS,收益大約2.8萬EOS。

針對這些惡意攻擊,有哪些防範措施?

1、在開發鏈上邏輯的時候按照一定的安全正規化,比如先改變變數再呼叫外部智慧合約。

2、使用成熟的安全庫,例如SafeMath庫,儘量不要自己造一些不太熟悉的底層庫輪子。

3、對關鍵業務操作要使用加鎖機制,設定暫停開關。好處是即使未來發生黑天鵝事件或者被攻擊,可以暫停自己的智慧合約,減少經濟損失。

4、不要使用鏈上資料做隨機數,因為攻擊者很可能對未來區塊裡的資訊做文章,導致隨機數可以被操縱。

5、在上線任何功能之前做第三方安全審計(至少兩家)。

6、當部署自己的鏈上邏輯後,需要對對應的智慧合約進行鏈上監控。這樣可以及時發現問題,儘早解決。

上面提到的攻擊,不管是重入攻擊、溢位攻擊、網路擁塞攻擊,攻擊者的意圖是惡意的。然而,還有一些攻擊是非惡意的,只是想把自己的利益最大化。這也是區塊鏈系統本身特有的攻擊發展方向,有點像傳統經濟學、金融學上的博弈論。

理性攻擊

下面我主要介紹一下區塊扣押攻擊、驗證者兩難攻擊。

區塊扣押攻擊

先從挖礦說起,大家都知道比特幣本身是使用工作量證明挖礦的。透過計算雜湊值,看前面有多少位是“0”,如果前若干位是“0”那麼礦工就挖出了新的區塊。如果礦工想獨自挖出新的區塊,硬體投入是相當大的,可能要有超過上億美金的投資去買礦機。

不同的礦工由於不能確定自己能獨立挖到區塊,就會聯合起來形成不同的礦池。礦池的優勢是會把難度進行分解,把大問題分解成小問題。

像Nonce,可以把“11”開頭的分給一個礦工,把“10”開頭的分給另一個礦工,每個礦工完成對應的計算後,把對應的結果返回給礦池的管理人。目前超過90%的礦工加入礦池,礦池的存在使得礦工收入更加穩定。例如你這邊有90個礦工,其中1個礦工挖到了最終區塊,那其他90個礦工可以按照自己的算力拿到對應的獎勵。

比特幣聯合挖礦協議看起來特別合理,礦工的算力高,拿的獎勵就高。對於礦工來說,遵守協議,直觀來說收益應該是最高的。

針對這樣的協議有沒有方式進行攻擊呢?這裡就要提到區塊扣押攻擊。在最初提出“區塊扣押攻擊”的時候,大家都不以為然,很多礦池運營人員都感覺不是一個大的問題。然而,之後卻發生了礦池受到扣押攻擊,造成了超過20萬美元的損失,讓大家意識到原來區塊扣押攻擊是真實存在的。

圖中大致介紹了礦工是可以參與到不同的礦池裡,可以參加左邊的礦池,也可以參加右邊的礦池,不同的礦池有不同的收益,根據你的算力獲得對應的獎勵。礦工其實可以把自己的算力資源分配給不同礦池,最終想要達到的效果就是利益最大化。那麼,如何將整個算力資源進行合理分配,然後利益最大化?

剛剛提到扣押攻擊就是一個例子。作為一個正常礦工,只要按照礦池分發不同的任務進行運算,把結果提交給礦池就可以了。然而區塊扣押攻擊是即使挖到獲得比特幣的區塊也不彙報給礦池,只是將之前算力算出來無效的結果給礦池進行彙報。由於礦工把之前算出來結果都給礦池進行分享,礦池還是按照礦工的算力支付對應的獎勵。

比特幣是零和遊戲,這樣做會不會讓其他礦池獲利更多,自己有損失呢?

現在請大家拿出紙和筆計算一個很簡單的例子,攻擊者擁有全網25%的算力,受害者礦池有75%的算力,對自己的算力進行分配,將5%的算力分配給礦池,剩下的20%自己挖礦。接下來開始區塊扣押攻擊,攻擊者在礦池佔了5%的算力,但是5%的算力其實是磨洋工,即使算到了對應的區塊,也不告訴礦池,所以礦池拿到的結果都是無效的。由於按照目前的協議,礦工只要算出來對應的結果給礦池,就可以拿到對應的獎勵,所以礦池還是會按照5%的算力分發獎勵。

但是由於這5%是磨洋工,全網的真實算力只有95%,對應的攻擊者佔了21%,礦池佔了79%。按照協議,右邊礦池拿79%獎勵,其中79%*5%=4.9%會分發給磨洋工的攻擊者。攻擊者本身正常挖礦也獲得了21%的獎勵,加起來25.9%。總結起來,如果攻擊者正常按照協議執行,25%的算力只能獲得25%的獎勵,但是如果實施區塊扣押攻擊,他就可以獲得25.9%的獎勵。

對區塊扣押攻擊進行進一步分析,例如考慮不同的情況,單一攻擊者,單一礦池;單一攻擊者,多個礦池;多個攻擊者,多個礦池。當進行大量運算後,我們發現如果攻擊者算力越高,獲得的獎勵越高。同時,如果只是單獨攻擊一個礦池,獎勵沒有攻擊多個礦池結果高。這和我們平時的直覺不太一樣,我們通常會感覺嚴格地遵循遊戲規則會獲利最大化。但是在這裡大家可以看到,礦工稍微改變挖礦方式實施區塊扣押攻擊,可以獲得更多的收益。

針對扣押攻擊,有哪些解決方案?其中一個方法是將同一任務分給多個礦工,一個礦工作弊,另一個礦工如果挖到比特幣的區塊會向礦池彙報。也可以改變償付計劃或者方式,例如給挖到比特幣區塊的礦工更多的獎勵,鼓勵礦工按照協議進行挖礦。當然,也可以改變比特幣的協議,使得原生挖礦可以支援礦池挖礦。

驗證者兩難問題

下面分享一下驗證者兩難問題。

曾經有一段時間,比特幣5%的礦工開採了無效區塊。大家發現原來很多礦工在沒有驗證區塊的情況下進行挖礦,導致越來越多人在無效區塊上建設新區塊,浪費了對應的算力,更容易導致51%攻擊。

像比特幣、以太坊,礦工的獎勵大部分是透過工作量證明獲得的,只有很少一部分是打包交易中的手續費。然而在做雜湊運算之前一個礦工要驗證對應每個區塊是否正確、交易是否正確,礦工作為驗證者是沒有任何獎勵的。這導致了礦工在進行挖礦時認為做雜湊運算是更重要的。資源枯竭攻擊又加強了礦工的這種想法。

在資源枯竭攻擊中,攻擊者將一些消耗大量運算資源的交易傳送給礦工,使得按照協議進行正常驗證交易的礦工花費大量時間進行驗證,而沒有驗證這些交易同時直接打包交易計算雜湊挖下一個區塊的礦工有了巨大的時間優勢(例如之前攻擊中驗證需要超過3分鐘)。

這就導致了“驗證者兩難問題”。如果礦工按照協議驗證交易和區塊,那麼他可以保證自己挖的區塊是沒有問題的,但同時可能由於驗證的時間過長,導致自己的挖礦時間晚於沒有做驗證的礦工,處於挖礦劣勢;而沒有做驗證的礦工,會提前打包交易有時間上的優勢挖下一個區塊,但是自己的區塊可能存在問題,問題區塊未來會被其他礦工或者節點丟棄造成自己算力的浪費。兩難選擇導致了礦工驗證者困境,到底應該驗證還是不驗證,如果大家都不驗證,那麼系統更容易受到51%攻擊。如果有些人驗證,有些人不驗證,不驗證的人會有更大的優勢去尋找下一個區塊。目前對於驗證兩難問題,還沒有比較好的解決方案。

隱私攻擊

最後,簡單介紹一下針對已有的隱私區塊鏈系統的攻擊。

匿名性是指在一組物件(匿名組)裡不能很好地單獨區分對應的物件。在比特幣的交易中,A轉賬給B,大家可以很明顯地看出來這筆交易的接收者是B。我們可以透過一些手段對接收者進行隱藏,例如Monero可以將A發給B的交易和其他交易(“Mixins”)進行混合,那麼大家就不能明確地區分出接收者是B還是CDE。透過將不同的交易進行混淆,讓大家不清楚到底A是發給B還是CDE。

早期Monero沒有強制每個傳送者都設定安全條件保護使用者隱私。這導致有些粗心的使用者以為Monero的隱私性是預設的,但實際沒有。而且由於匿名組不夠大,透過一些簡單的推算分析可以很快推算出很多交易的接收者。如圖最下面的紫色箭頭,C只混合了兩筆交易,由於上面綠色箭頭A已經發給B了,接下來C就不可能發給B,肯定是發給D。中間的黑色箭頭雖然混淆了很多交易,但是由於之前的幾筆交易已經暴露出來,我們可以推算出最終的接收者是E。

在過去一段時間裡,特別是早期Monero系統裡,有超過64%的傳送者沒有使用Mixins設定多個接收者,超過63%的傳送者即使設定了多個,但是可以根據剛剛的方法推算出接收者。當然,隨著Monero隱私保護功能的逐步增強,用已有的攻擊方法是更難推算出來接收者的。

大家都知道Monero本身的隱私性不是特別好,在區塊鏈世界裡誰的隱私性相對最好?就是Zcash,Zcash是使用零知識證明來保護髮送者和接收者的身份以及傳送金額。

圖中有幾種特殊交易:透明交易,A發給B,大家知道數目,也知道傳送者、接收者。之後B發給C,進行匿名化。由於有匿名池,C再發給DEFG的匿名交易,大家都分辨不出來到底誰是真正的接收者。但是,DEFG有時候可能要去匿名化,把匿名身份轉為非匿名身份。

有意思的是Zcash裡超過85%的交易都是透明的,入金/出金很容易推算出來的。有少於1%的交易是匿名交易的,Zcash本身使用零知識證明,目前來講零知識證明是比較安全的匿名化方法。

很多研究者、攻擊者會把精力放在怎麼透過匿名化、去匿名化交易推斷出地址關聯性。有哪些人在使用匿名交易池呢?

1、礦工,分獨立礦工和礦池。由於Zcash每個區塊有10ZEC的獎勵,那麼很容易區分出區塊的獎勵者、接收者的地址。

2、創始人。每個區塊有2.5ZEC獎勵,地址是公開的,大家也可以區分出來。

3、個人使用者等。看入金進入匿名交易池的地址是很容易區分的,有大於80%的入金匿名化交易來自礦工,創始人也有很清晰的交易步驟。礦工直接從區塊拿到獎勵,地址相對容易暴露出來。從匿名交易池出來的地址不容易區分,大於90%去匿名化的交易地址很難區分。

透過一些分析,攻擊者發現大家使用Zcash匿名化的過程中有一些很有意思的操作,例如創始人行為。很多情況下創始人入金都是249.999ZEC,出金是250.001ZEC。如果僅從對應的數值來看,大家很容易區別出哪些地址在進行匿名化操作時候是來自創始人的,哪些去匿名化的地址也是來自於創始人的,因為數值可以進行繫結。

礦池也有一個很有意思的行為,礦池會把資金轉入匿名交易池,然而在分給礦工獎勵的時候最終還會有自己的地址。這導致了很容易辨認的模式,攻擊者可以分析出已知礦池地址,同時還有上百個其他地址,那麼這筆交易肯定是屬於礦池和礦工的。

不管是Zcash還是Monero,很少人能夠很好地使用隱私保護系統以及手段。很多時候大家覺得自己用了隱私保護系統,那麼保護效果就應該很好。但其實不是的,每個系統都有一些對應的設定,如果你作為使用者沒有設定好的話,那對應的交易是不能被很好地保護起來的。

針對Zcash有69%的匿名化交易是可以根據剛剛的模式區分出來,但是Zcash的底層密碼學技術是安全的,可以提供很高的匿名性以及隱私性。前提是大家能夠比較正確地進行匿名化操作,保證自己沒有使用那些明顯的模式,而被攻擊者、研究者發現出來。

“在區塊鏈的世界裡,除了有破壞規則的惡意攻擊者,還有很多利用規則的利益最大化者。”

例如區塊扣押攻擊以及利用區塊鏈平臺的擁塞攻擊。大家沒有破壞規則,只是利用區塊鏈本身的規則/平臺特性來達到利益最大化。我們在設計區塊鏈系統和應用時,最好能夠兼顧自己的系統或者程式是沒有漏洞給惡意攻擊者進行破壞,同時儘量防止利用協議/規則的損人利己的利益最大化行為。

最後,希望與大家一起創造出更好的方法,共建安全的Web3.0生態。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;