從區塊鏈中常見的攻擊類型談區塊鏈的隱私與安全

本文整理自Parity亞洲技術總監賈瑤琪先生在萬向區塊鏈蜂巢學院直播間進行的Web 3.0訓練營公開課。

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

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

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

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

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

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

 

下面我們就來認識一下區塊鏈中常見的攻擊類型。

第一類:惡意攻擊

 

(1)重入攻擊
 

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再做交互轉賬,就可以避免這樣的攻擊了。這就是一個比較好的規範,先去檢查,然後執行,最後再做交互。

 

(2)溢出攻擊

程序員們都清楚,不管是用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。

 

(3)所有權攻擊


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

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

 

(4)擁塞攻擊


除了利用智能合約漏洞的攻擊外,還有些攻擊其實是利用區塊鏈本身的特性進行攻擊。其中最有意思的一類攻擊叫做“擁塞攻擊”。比如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、當部署自己的鏈上邏輯後,需要對對應的智能合約進行鏈上監控。這樣可以及時發現問題,儘早解決。

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

第二類:理性攻擊
 

(1)區塊扣押攻擊

先從挖礦說起,大家都知道比特幣本身是使用工作量證明挖礦的。通過計算哈希值,看前面有多少位是“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%的獎勵。

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

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

 

(2)驗證者兩難問題


曾經有一段時間,比特幣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生態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章