在傳統(tǒng)的client-server網(wǎng)路架構(gòu)中,存在著所謂的中間人攻擊:攻擊者可以攔截通訊雙方的通話并插入新的內(nèi)容。而在區(qū)塊鏈的世界中,也存在著類似的攻擊手法,稱為日蝕攻擊。本文將以波士頓大學(xué)團(tuán)隊(duì)的一篇論文為基礎(chǔ),對(duì)日蝕攻擊進(jìn)行一個(gè)基本的介紹。
這篇介紹文預(yù)計(jì)分成兩部分,第一部分(即本文)將會(huì)對(duì)Ethereum的P2P做一個(gè)簡(jiǎn)單的介紹,如此才能了解日蝕攻擊為何能夠生效。第二部分將介紹波士頓大學(xué)團(tuán)隊(duì)論文中關(guān)于日蝕攻擊的實(shí)現(xiàn)原理。
在撰寫此篇文章的時(shí)候,最新geth版本為1.8.0,而日蝕攻擊論文的實(shí)驗(yàn)對(duì)象為geth 1.6.6 ,本文所描述的各種Ethereum環(huán)境,若無特別提及,則視作與日蝕攻擊論文一致。
除此之外,Ethereum的重大改版在即,此文中所描述的諸多Ethereum P2P特性都可能面臨巨大的改變,請(qǐng)多加注意。
Ethereum的P2P特色
根據(jù)日蝕攻擊此篇論文的整理,Ethereum的P2P有以下幾點(diǎn)特色(A~G):
A. 基于Kademlia設(shè)計(jì)的P2P協(xié)定
Kademlia是一種用于檔案共享的結(jié)構(gòu)化P2P協(xié)定,其目標(biāo)為有效率的儲(chǔ)存及定位檔案。BitTorrent以及eMule等檔案共享工具就是基于Kademlia設(shè)計(jì)而成。
在Kademlia的協(xié)定中,每一個(gè)檔案以及每一個(gè)節(jié)點(diǎn)都被賦予一個(gè)唯一的識(shí)別碼( ID ),這個(gè)ID是以長度為b的位元組所形成(在原始的Kademlia中,b =160)。此ID會(huì)用來計(jì)算所謂的距離參數(shù)( d,distance,個(gè)人習(xí)慣形容為相異度 ),d值越大表示距離越遠(yuǎn)(相異度越大),計(jì)算方式是對(duì)兩個(gè)ID ( ID?及ID? )進(jìn)行異或運(yùn)算 ( XOR ),其定義如下:
d = ID ? ⊕ ID ?
一個(gè)簡(jiǎn)單范例如下:
ID? = 10011.。.01
ID? = 10011.。.10
ie ID?及ID?的前158個(gè)位元皆一致
d = ID ? ⊕ ID ? = 10011.。.01 ⊕ 10011.。.10 = 00000.。. 11 = 3
另外,在每一個(gè)Kademlia的節(jié)點(diǎn)里,包含一種稱之為桶(bucket)的資料結(jié)構(gòu)。桶里所儲(chǔ)存的是網(wǎng)路中的節(jié)點(diǎn)資訊,桶的數(shù)量即為識(shí)別碼的長度( b ),每一個(gè)桶里最多會(huì)儲(chǔ)存k個(gè)節(jié)點(diǎn)的資訊,因此這種資料結(jié)構(gòu)又被稱為k桶( k-bucket ),如下圖:
Ethereum沿用了Kademlia的XOR運(yùn)算及bucket概念,另外在Ethereum的設(shè)計(jì)中,尋找檔案的機(jī)制不再需要,只留下更新節(jié)點(diǎn)資訊的機(jī)制,詳細(xì)內(nèi)容可參考下面敘述(E.節(jié)點(diǎn)資訊的更新方式) 。
在此篇論文中,作者提到無法理解Ethereum的P2P協(xié)定為何要使用Kademlia。
由于Ethereum中的底層資料結(jié)構(gòu)(chaindata等資料),并未分割成不同的小片段去作分散式儲(chǔ)存,因此也就無法利用到Kademlia中尋找檔案的特性。
個(gè)人猜測(cè)或許是Ethereum當(dāng)初在設(shè)計(jì)開發(fā)時(shí),曾經(jīng)有考慮要將底層資料結(jié)構(gòu)進(jìn)行分散式儲(chǔ)存?此點(diǎn)還待高手協(xié)助解惑。
B. ECDSA公鑰作為節(jié)點(diǎn)ID
Ethereum以ECDSA公鑰作為節(jié)點(diǎn)ID,其ID長度為512bits。同一臺(tái)機(jī)器(同一個(gè)IP)可以運(yùn)行數(shù)個(gè)Ethereum節(jié)點(diǎn),亦即一個(gè)IP可以產(chǎn)生數(shù)個(gè)節(jié)點(diǎn)ID,這是日蝕攻擊能夠生效的因素之一。
C. UDP協(xié)定與TCP協(xié)定負(fù)責(zé)不同層級(jí)的訊息交換
在Ethereum中,P2P資訊的交換是藉由UDP來進(jìn)行,而區(qū)塊資訊的交換則是在TCP上進(jìn)行。
Ethereum UDP所傳遞的資訊可分為四種:ping,pong,findnode以及neighbor。
· ping and pong: ping訊息用來嘗試探知某個(gè)節(jié)點(diǎn),若是該節(jié)點(diǎn)接受到 ping訊息即會(huì)返回 pong訊息。
· findnode and neighbor:findnode訊息會(huì)附帶一個(gè)節(jié)點(diǎn)ID(假設(shè)為 t ), findnode將會(huì)向另一個(gè)節(jié)點(diǎn)(假設(shè)為 x )詢問距離 t最近的節(jié)點(diǎn)資訊,亦即詢問 x的節(jié)點(diǎn)列表中距離t最近的節(jié)點(diǎn)。x會(huì)將這些距離 t最近的節(jié)點(diǎn)資訊,以 neighbor訊息返回,最多會(huì)返回16筆節(jié)點(diǎn)資訊。
為了防御重放攻擊(replay attacks),UDP傳遞的這些資訊,都會(huì)加上時(shí)間戳記(timestamp),當(dāng)節(jié)點(diǎn)發(fā)現(xiàn)某一個(gè)UDP訊息其時(shí)間比本機(jī)時(shí)間還老舊(比本機(jī)時(shí)間早20秒) ,就會(huì)丟棄該訊息。
Ethereum的TCP連線可分為兩種,一種是由本機(jī)節(jié)點(diǎn)發(fā)出請(qǐng)求給其他節(jié)點(diǎn)的outgoing連線,另一種是其他節(jié)點(diǎn)發(fā)起請(qǐng)求給本機(jī)節(jié)點(diǎn)的incoming連線。一個(gè)Ethereum節(jié)點(diǎn)同一時(shí)間最多可允許25個(gè)TCP連線存在( maxpeers ),而在geth 1.8.0版之前,這25個(gè)TCP連線可以全部都是incoming連線,這也是日蝕攻擊能夠生效的其中一個(gè)因素。
D. 儲(chǔ)存P2P節(jié)點(diǎn)資訊的資料結(jié)構(gòu)
Ethereum有兩種儲(chǔ)存節(jié)點(diǎn)資訊的資料結(jié)構(gòu),一種用于長期儲(chǔ)存,日蝕攻擊論文的作者稱之為db。db會(huì)將節(jié)點(diǎn)資訊持久化的儲(chǔ)存于硬碟內(nèi),因此每次節(jié)點(diǎn)重啟時(shí),db的資料仍會(huì)保存。db內(nèi)儲(chǔ)存了數(shù)筆節(jié)點(diǎn)的資訊,每一筆資訊包含以下的欄位:
· nodeIP:節(jié)點(diǎn)IP
· tcpPort:TCP Port number
· udpPort:UDP Port number
· latestPing:最近一次嘗試接觸該節(jié)點(diǎn)的時(shí)間(亦即ping訊息送出的時(shí)間)
· latestPong:最近一次該節(jié)點(diǎn)回應(yīng)的時(shí)間(亦即收到pong訊息的時(shí)間)
· failedTimes:以findnode嘗試詢問該節(jié)點(diǎn)卻失敗的次數(shù)
以上欄位名稱并非Ethereum geth原始碼中所定義的名稱,只是為了方便本文后續(xù)說明而定義的名稱。
Ethereum本機(jī)節(jié)點(diǎn)會(huì)定期檢查db內(nèi)的每筆節(jié)點(diǎn)資訊,若是某節(jié)點(diǎn)的latestPong與當(dāng)下時(shí)間間隔已經(jīng)超過一天,則將該節(jié)點(diǎn)從db中移除。
另一種結(jié)構(gòu)是短期儲(chǔ)存,稱之為table,每次重啟節(jié)點(diǎn)時(shí),table都會(huì)是空的。table含有256個(gè)bucket,每個(gè)bucket又包含16筆其他節(jié)點(diǎn)的資料,每筆資料的欄位如下:
· nodeID:節(jié)點(diǎn)ID
· nodeIP:節(jié)點(diǎn)IP
· tcpPort:TCP Port number
· udpPort:UDP Port number
bucket內(nèi)的每筆節(jié)點(diǎn)資料都會(huì)按照加入順序而排序,當(dāng)一個(gè)bukcet已經(jīng)額滿卻還有新節(jié)點(diǎn)資料要加入的時(shí)候,本機(jī)節(jié)點(diǎn)會(huì)傳送ping訊息給bukcet中最早加入的節(jié)點(diǎn),若是最早加入的節(jié)點(diǎn)沒有回應(yīng)pong訊息,則移除最早加入的節(jié)點(diǎn),并將新節(jié)點(diǎn)加入,反之則丟棄新節(jié)點(diǎn)。
為了決定某一個(gè)節(jié)點(diǎn)資訊要放到哪一個(gè)bucket之中,需配合logdist函式來進(jìn)行計(jì)算,過程如下,其中ID?及ID?是logdist函式的輸入?yún)?shù):
H? = SHA3(ID?) ,where ID? is the ID of local node
H? = SHA3(ID?) ,where ID? is the ID of other node
r = similarity(H?,H?) ,where 0 ≤ r ≤ 255
i.e. r is number of most significant bits that H? and H? are the same
經(jīng)過上面的計(jì)算得到r值后,就可算出節(jié)點(diǎn)會(huì)被儲(chǔ)存在編號(hào)為bucket_num (等于256﹣ r )的bucket之內(nèi)。由于logdist函式的特性,節(jié)點(diǎn)在不同bucket_num的分配會(huì)呈現(xiàn)高度偏移(highly-skewed)的狀況,即任一節(jié)點(diǎn)有較高機(jī)率被分配到bucket_num較大的bucket中,其機(jī)率分布如下式:
P? = 1 / ( 2^( r +1) ) ,where 0 ≤ r ≤ 255 , bucket_num = 256﹣ r
根據(jù)上式,機(jī)率分布圖如下:
理論上table結(jié)構(gòu)最大可容納4096筆節(jié)點(diǎn)資料(一個(gè)bucket最多容納16筆,共有256個(gè)bucket ) ,但是由于這種高度偏移狀況,實(shí)際上table大部分的時(shí)間只會(huì)儲(chǔ)存少量的節(jié)點(diǎn)資料(大約150~200筆,參考下圖,出自日蝕攻擊論文)。
E. 節(jié)點(diǎn)資訊的更新方式
Ethereum綜合了下述的預(yù)設(shè)節(jié)點(diǎn)資訊以及運(yùn)算模式,以新增或更新其節(jié)點(diǎn)資訊紀(jì)錄( table及db ) :
1.預(yù)設(shè)節(jié)點(diǎn)( Bootstrap nodes)
Ethereum內(nèi)建(hardcoded)了六筆預(yù)設(shè)節(jié)點(diǎn)資訊。當(dāng)一個(gè)Ethereum節(jié)點(diǎn)初始化并第一次啟動(dòng)時(shí),db沒有儲(chǔ)存任何資料,此時(shí)就會(huì)用到預(yù)設(shè)節(jié)點(diǎn)資訊。
2.聯(lián)結(jié)更新運(yùn)算( Bonding)
聯(lián)結(jié)更新運(yùn)算是本機(jī)節(jié)點(diǎn)嘗試新增或更新某一個(gè)遠(yuǎn)端節(jié)點(diǎn)資訊的過程( db以及table ),其具體過程如下:
step1-a:檢查該遠(yuǎn)端節(jié)點(diǎn)是否已存在于db中
step1-b:檢查failedTimes是否為0
step1-c:檢查latestPong是否小于24小時(shí)
step2:若是第一個(gè)步驟的三個(gè)條件都成立,則將該節(jié)點(diǎn)存入table中對(duì)應(yīng)的bucket,具體存入過程請(qǐng)參考上述D.儲(chǔ)存P2P節(jié)點(diǎn)資訊的資料結(jié)構(gòu)。 若是有任一條件不成立,則進(jìn)到step3。
step3:嘗試傳送ping訊息給該節(jié)點(diǎn),若收到該節(jié)點(diǎn)的pong訊息,則于db加入或更新該節(jié) 點(diǎn)資訊,并且將該節(jié)點(diǎn)存入table中對(duì)應(yīng)的bucket。
3. ping訊息觸發(fā)運(yùn)算( Unsolicited pings )
當(dāng)本機(jī)節(jié)點(diǎn)收到一個(gè)遠(yuǎn)端節(jié)點(diǎn)傳送過來的ping訊息時(shí),除了回應(yīng)pong訊息給該遠(yuǎn)端節(jié)點(diǎn),本機(jī)節(jié)點(diǎn)也會(huì)針對(duì)該遠(yuǎn)端節(jié)點(diǎn)執(zhí)行上述的聯(lián)結(jié)更新運(yùn)算( Bonding )。
4.尋訪運(yùn)算( Lookup )
尋訪運(yùn)算即針對(duì)某一個(gè)目標(biāo)節(jié)點(diǎn)找尋其最近節(jié)點(diǎn),并且紀(jì)錄這些最近節(jié)點(diǎn),步驟如下:
step1:給定一個(gè)目標(biāo)節(jié)點(diǎn),稱之為t。
step2:從本機(jī)節(jié)點(diǎn)table中的所有節(jié)點(diǎn)中選出16個(gè)最接近t (根據(jù)logdist函式)的節(jié)點(diǎn),
這16個(gè)節(jié)點(diǎn)組成“待訪節(jié)點(diǎn)列表”。
step3:本機(jī)分別去訪問( findnode )“待訪節(jié)點(diǎn)列表”的每個(gè)節(jié)點(diǎn),每一個(gè)被訪問的節(jié)點(diǎn)會(huì)再
各自返回多個(gè)(最多16個(gè))更接近t的節(jié)點(diǎn)( neighbor )。
step4: step3完成后,本機(jī)可得到數(shù)個(gè)新節(jié)點(diǎn)(最多256個(gè)),接著對(duì)這數(shù)個(gè)新節(jié)點(diǎn)進(jìn)行聯(lián)結(jié)
更新運(yùn)算( bonding )。
step5:從step2所選出的16個(gè)節(jié)點(diǎn)加上step4完成聯(lián)結(jié)更新運(yùn)算的新節(jié)點(diǎn)(最多16+256
個(gè))中再度組成新的待訪節(jié)點(diǎn)列表,并取代step2原本的待訪節(jié)點(diǎn)列表。
step6:本機(jī)不斷重復(fù)step2 ~ step5,直到節(jié)點(diǎn)資訊不再變化,表示尋訪機(jī)制完成(當(dāng)
step5新的待訪節(jié)點(diǎn)列表與step2原本的待訪節(jié)點(diǎn)列表相同時(shí),視作不再變化),此
時(shí)會(huì)將這組最新的待訪節(jié)點(diǎn)列表放到一個(gè)lookup_buffer的資料結(jié)構(gòu)中。
F. 種子運(yùn)算(Seeding)
種子運(yùn)算皆由本機(jī)自主觸發(fā),是以近期之內(nèi)保持在線上的節(jié)點(diǎn)資訊作為更新table資訊的依據(jù),具體行為是將預(yù)設(shè)節(jié)點(diǎn)以及db之中的節(jié)點(diǎn)資訊復(fù)制到table中,有三種情況會(huì)觸發(fā)種子運(yùn)算:
· 當(dāng)Ethereum節(jié)點(diǎn)啟動(dòng)時(shí)
· 每一小時(shí)定期觸發(fā)
· 尋訪運(yùn)算( Lookup )啟動(dòng)時(shí)
種子運(yùn)算的具體步驟如下:
step1:本機(jī)節(jié)點(diǎn)檢查是否table為空;若為空,才繼續(xù)執(zhí)行后續(xù)步驟。
step2:本機(jī)節(jié)點(diǎn)對(duì)六筆預(yù)設(shè)節(jié)點(diǎn)執(zhí)行聯(lián)結(jié)更新運(yùn)算。
step3:從本機(jī)db中,取出latestPong小于或等于120小時(shí)的一組節(jié)點(diǎn),若這一組節(jié)點(diǎn)的數(shù)
量少于30個(gè),則全部保留;若數(shù)量多于30個(gè),則隨機(jī)留下其中30個(gè)。接著本機(jī)節(jié)點(diǎn)針
對(duì)這些節(jié)點(diǎn)去執(zhí)行聯(lián)結(jié)更新運(yùn)算。
step4:本機(jī)節(jié)點(diǎn)以自己為目標(biāo)執(zhí)行尋訪運(yùn)算。
G. Outgoing TCP連線的建立
Ethereum節(jié)點(diǎn)會(huì)持續(xù)建立Outgoing TCP連線,Outgoing TCP連線數(shù)最多可達(dá)13個(gè)(0.5×(1+ maxpeers ))。Ethereum節(jié)點(diǎn)會(huì)準(zhǔn)備兩種執(zhí)行緒:
第一種執(zhí)行緒是discover_task,此執(zhí)行緒之任務(wù)是以一個(gè)隨機(jī)節(jié)點(diǎn)為目標(biāo)去進(jìn)行尋訪運(yùn)算以持續(xù)更新節(jié)點(diǎn)資訊。
第二種執(zhí)行緒是dial_task,此執(zhí)行緒之任務(wù)用于對(duì)某個(gè)目標(biāo)節(jié)點(diǎn)建立TCP連線,嘗試建立連線前,首先會(huì)檢查幾個(gè)條件:
· 該節(jié)點(diǎn)不在黑名單內(nèi)
· 與該節(jié)點(diǎn)的TCP連線尚未建立
· 是否正在對(duì)該節(jié)點(diǎn)進(jìn)行撥號(hào)( dial );若否,則成立
· 是否最近曾對(duì)該節(jié)點(diǎn)進(jìn)行撥號(hào);若否,則成立
整體而言,Ethereum會(huì)由lookup_buffer (經(jīng)由尋訪運(yùn)算得出)及table取得節(jié)點(diǎn)資訊,再嘗試去和這些節(jié)點(diǎn)建立TCP連線。
綜合上述,可以整理出Ethereum更新節(jié)點(diǎn)資訊以及連結(jié)節(jié)點(diǎn)的過程,如下圖:
以上是關(guān)于Ethereum P2P的簡(jiǎn)介,而日蝕攻擊便是針對(duì)Ethereum建立節(jié)點(diǎn)資訊和連結(jié)節(jié)點(diǎn)的過程進(jìn)行攻擊。
日蝕攻擊手法
接下來的敘述中,只要受害目標(biāo)的所有TCP連線都被攻擊者占據(jù),我們都定義為完成日蝕攻擊。
A. Monopolizing Connections — 主動(dòng)占據(jù)受害目標(biāo)(victim)的連線
此攻擊的概念就是攻擊者主動(dòng)去占據(jù)受害目標(biāo)的所有TCP連線。這種攻擊能夠成功的原因,是基于Ethereum節(jié)點(diǎn)的幾個(gè)特性:
· 一個(gè)節(jié)點(diǎn)的所有TCP連線可以全部是Incoming連線,也就是由其他遠(yuǎn)端節(jié)點(diǎn)發(fā)起請(qǐng)求所形成的連線。
· 當(dāng)一個(gè)節(jié)點(diǎn)重啟時(shí),Outgoing及Incoming的連線數(shù)皆為0。
· 當(dāng)一個(gè)節(jié)點(diǎn)重啟時(shí),需要一段頗長的時(shí)間才會(huì)建立起Outgoing連線。
根據(jù)論文的實(shí)驗(yàn)數(shù)據(jù)顯示,在一臺(tái)2 vCPU及2GB RAM的云端服務(wù)器上啟動(dòng)Ethereum節(jié)點(diǎn),要8秒鐘之后種子運(yùn)算才會(huì)開始進(jìn)行,也就是8秒鐘之后才會(huì)開始嘗試進(jìn)行Outgoing TCP連線。
攻擊細(xì)節(jié)如下:首先,攻擊者會(huì)產(chǎn)生N個(gè)節(jié)點(diǎn)ID,N的數(shù)量遠(yuǎn)大于一個(gè)Ethereum節(jié)點(diǎn)所能允許的最大TCP連線數(shù)( maxpeers,預(yù)設(shè)25個(gè))。接下來,等到受害節(jié)點(diǎn)重啟之后,立即以這N個(gè)節(jié)點(diǎn)對(duì)受害節(jié)點(diǎn)進(jìn)行Incoming連線。當(dāng)受害者的全部TCP連線都被攻擊者的Incoming連線占據(jù)時(shí),即完成日蝕攻擊。
論文作者嘗試了兩個(gè)實(shí)驗(yàn),在第一個(gè)實(shí)驗(yàn)中,攻擊者在兩臺(tái)主機(jī)上建立了1000個(gè)攻擊節(jié)點(diǎn),接著重啟受害者節(jié)點(diǎn)再去攻擊。這個(gè)實(shí)驗(yàn)重復(fù)了50次,每次都會(huì)將受害者回復(fù)到攻擊前的狀態(tài)。最后發(fā)現(xiàn)50次攻擊里有49次可以完成日蝕攻擊。在失敗的那一次中,受害者節(jié)點(diǎn)成功建立了一條Outgoing連線。
在第二個(gè)實(shí)驗(yàn)中,論文作者測(cè)試了網(wǎng)路延遲(latency)的影響。作者這次將受害者節(jié)點(diǎn)安排在距離攻擊者節(jié)點(diǎn)較遠(yuǎn)的地方(受害者在新加坡,攻擊者則在紐約),接著一樣建立1000個(gè)攻擊節(jié)點(diǎn),最后發(fā)現(xiàn)53次的攻擊里只有43次會(huì)完成日蝕攻擊。
根據(jù)實(shí)驗(yàn)結(jié)果,作者認(rèn)為Ethereum節(jié)點(diǎn)的Incoming連線沒有限制數(shù)量是關(guān)鍵弱點(diǎn)所在,因此建議應(yīng)該限制Incoming連線數(shù),以保證Ethereum節(jié)點(diǎn)能夠進(jìn)行Outgoing連線。
這一個(gè)建議已經(jīng)被Ethereum社群所采納,在geth 1.8.0版本中, Incoming連線的數(shù)量變成是可以限制的,預(yù)設(shè)上限是maxpeers /3。
B. Table Poisoning — 竄改受害者的節(jié)點(diǎn)資訊列表
在第一種攻擊中,作者在最后提出了防御補(bǔ)強(qiáng)的建議,假設(shè)受害者采納了這個(gè)建議,使得本機(jī)能夠限制Incoming連線數(shù),那么攻擊者是否仍有機(jī)會(huì)完成日蝕攻擊?
在這種前提下,作者設(shè)想了另一種可能的攻擊方式— Table Poisoning。在Table Poisoning這種攻擊手法中,除了主動(dòng)占據(jù)受害者的Incoming連線,攻擊者還必須設(shè)法侵占受害者的節(jié)點(diǎn)資訊列表,使其列表中的大多數(shù)節(jié)點(diǎn)都是屬于攻擊者所控制的節(jié)點(diǎn),那么當(dāng)受害者嘗試進(jìn)行Outgoing連線時(shí),仍然會(huì)連接到攻擊者的節(jié)點(diǎn),借此達(dá)成占據(jù)其所有TCP連線的意圖,此攻擊經(jīng)由以下幾個(gè)步驟完成。
首先第一步,由于攻擊者知道受害者的節(jié)點(diǎn)ID,攻擊者便可計(jì)算出受害者table的不同bucket中,可能會(huì)儲(chǔ)存哪些節(jié)點(diǎn)資訊( logdist函式)。由于Ethereum 節(jié)點(diǎn)儲(chǔ)存方式的特性,攻擊者并不需要占滿受害者的所有bucket,攻擊者只要嘗試占滿受害者的最后n個(gè)bucket ( bucket_num從256~256﹣ n ),即可以極高的機(jī)率占據(jù)其TCP連線。所以攻擊者的第一步工作,便是去算出多個(gè)匹配bucket_num的攻擊用節(jié)點(diǎn)ID (以下簡(jiǎn)稱IDattack )。
可預(yù)期的是當(dāng)n越大,需要越多時(shí)間去算出能夠匹配bucket_num的IDattack。
在本論文中,作者選擇n=17作為其bucket攻擊數(shù)量,總共需要計(jì)算出272個(gè)IDattack,作者使用一臺(tái)MacBook Pro進(jìn)行計(jì)算(CPU:i5 2.9 GH;RAM:16 GB),總共需要15分鐘去算出272個(gè)IDattack。
接下來第二步,在計(jì)算出IDattack之后,便是設(shè)法讓這些IDattack被插入到受害者的db之中。攻擊者會(huì)藉由這些IDattack發(fā)出ping訊息給受害者,受害者收到ping訊息之后,除了回應(yīng)pong訊息,還會(huì)對(duì)這些IDattack進(jìn)行Bonding,如此便可讓這些IDattack被插入到受害者的db之中。
在這個(gè)過程中,攻擊者每24小時(shí)便會(huì)由這些IDattack發(fā)出ping訊息給受害者,并且攻擊者也要確實(shí)回應(yīng)受害者的ping訊息(返回pong )以及findnode訊息(攻擊者會(huì)返回空的neighbor訊息),如此這些IDattack便具備了快速占據(jù)受害者table的條件(詳細(xì)內(nèi)容請(qǐng)參考Bonding運(yùn)算)。
第三步是此攻擊的最后步驟,設(shè)法讓受害者節(jié)點(diǎn)重新啟動(dòng)。Ethereum節(jié)點(diǎn)重新啟動(dòng)時(shí),其table是空的,攻擊者在這時(shí)會(huì)不斷的藉由已經(jīng)產(chǎn)生的IDattack發(fā)出ping訊息給受害者,由于在第二步中,我們使這些IDattack具備了快速占據(jù)受害者table的條件,此時(shí)便可很快的占據(jù)受害者table。
Ethereum節(jié)點(diǎn)在啟動(dòng)時(shí),UDP監(jiān)聽程序會(huì)先運(yùn)作(此時(shí)就可以連接其他節(jié)點(diǎn)的Incoming連線,并接受其他節(jié)點(diǎn)傳來的ping訊息進(jìn)行Bonding運(yùn)算),然后才進(jìn)行種子運(yùn)算程序,兩個(gè)程序之間大約間隔一秒。
藉由種子運(yùn)算的特性,我們可以得知,在table已存有節(jié)點(diǎn)資訊的情況下,種子運(yùn)算并不會(huì)發(fā)生作用,也就是db中沒有任何節(jié)點(diǎn)資訊會(huì)轉(zhuǎn)移到table中(妨礙種子運(yùn)算),如此受害者的Outgoing連線將全部連接到攻擊者的節(jié)點(diǎn),到這個(gè)時(shí)候,攻擊者只要再配合Monopolizing Connections攻擊的方法想辦法占據(jù)剩余的Incoming連線,就有機(jī)會(huì)完成日蝕攻擊。
table的更新可能是外部觸發(fā)(Unsolicited pings ,遠(yuǎn)端節(jié)點(diǎn)發(fā)出ping給本機(jī))或是自主觸發(fā)(本機(jī)的種子運(yùn)算)。Table Poisoning的作用是嘗試讓自主觸發(fā)失效。
IDattack只是具備快速占據(jù)受害者table的條件,并無法完全保證table只儲(chǔ)存IDattack。若是有其他誠實(shí)節(jié)點(diǎn)嘗試發(fā)出ping訊息給受害者,table中就有可能儲(chǔ)存了誠實(shí)節(jié)點(diǎn)。
針對(duì)這種攻擊,作者也執(zhí)行了兩種實(shí)驗(yàn)。第一個(gè)實(shí)驗(yàn),攻擊者先準(zhǔn)備了一個(gè)已經(jīng)運(yùn)行33天的受害者(地點(diǎn)在紐約),此受害者的db中儲(chǔ)存了25580筆節(jié)點(diǎn)資訊,在那個(gè)時(shí)間點(diǎn),這已經(jīng)可以視為Ethereum全網(wǎng)的全部節(jié)點(diǎn)數(shù)量。接著作者依上述步驟開始進(jìn)行攻擊,第一步和第二步各執(zhí)行一次,第三步則重復(fù)執(zhí)行51次,每次重復(fù)之前都會(huì)將受害者主機(jī)重新啟動(dòng)。
攻擊者準(zhǔn)備了兩臺(tái)攻擊用主機(jī),一臺(tái)主機(jī)(位于波士頓)以272個(gè)IDattack傳送ping訊息給受害者,另一臺(tái)主機(jī)(位于紐約)則依照Monopolizing Connections攻擊以1000個(gè)攻擊節(jié)點(diǎn)對(duì)受害者進(jìn)行Incoming連線。在這51次的實(shí)驗(yàn)里,有49次受害者的Outgoing連線皆被攻擊者占據(jù)(其中只有19次成功地妨礙種子運(yùn)算),然而這51次中卻只有34次完成日蝕攻擊。
為何此次攻擊的成功率明顯較為低落(比起單純Monopolizing Connections攻擊)?
作者解釋到,由于此實(shí)驗(yàn)仍是使用舊版geth來進(jìn)行(在他們實(shí)驗(yàn)的時(shí)候,尚未發(fā)行g(shù)eth 1.8.0,也就是尚未發(fā)行針對(duì)第一種攻擊進(jìn)行修正的版本),為了模擬出限制Incoming連線數(shù)的效果(也就是25個(gè)TCP連線中,一部分必須是屬于Outgoing),攻擊者一開始不會(huì)積極搶占受害者的Incoming連線,直到確認(rèn)受害者建立了Outgoing連線,才會(huì)嘗試搶占受害者的Incoming連線,而在這段等待確認(rèn)Outgoing的時(shí)間,受害者可能已經(jīng)連接了其他誠實(shí)節(jié)點(diǎn)的Incoming連線,導(dǎo)致日蝕攻擊無法完成。
而在第二個(gè)實(shí)驗(yàn)中,作者測(cè)試了網(wǎng)路延遲(latency)的影響。攻擊者準(zhǔn)備了一個(gè)已經(jīng)運(yùn)行1小時(shí)的受害者(地點(diǎn)在新加玻),此受害者的db中儲(chǔ)存了7000筆節(jié)點(diǎn)資訊。兩臺(tái)攻擊用主機(jī)則與第一個(gè)實(shí)驗(yàn)相同,不再贅述。第二個(gè)實(shí)驗(yàn)重復(fù)了50次,最后有44次完成了日蝕攻擊,6次的失敗中,有1次是因?yàn)槭芎φ呓⒘薕utgoing連線,另外5次則是其他誠實(shí)節(jié)點(diǎn)建立了Incoming連線。
在實(shí)際應(yīng)用上,Table Poisoning仍有缺陷。為了匹配某個(gè)受害者的節(jié)點(diǎn)ID去產(chǎn)生攻擊用節(jié)點(diǎn)ID,攻擊者需要耗費(fèi)可觀的運(yùn)算資源,如果攻擊者想攻擊多個(gè)不同的受害者,就必須產(chǎn)生大量的攻擊用節(jié)點(diǎn)ID,資源的消耗也會(huì)急遽增加。
針對(duì)這種資源消耗的缺陷,作者設(shè)想了另一種產(chǎn)生攻擊用節(jié)點(diǎn)ID的方式,稱之為建立尋訪表( lookup table )。
針對(duì)Table Poisoning的攻擊手法,作者建議了幾種補(bǔ)強(qiáng)方式,大致上可區(qū)分為兩種策略—改良節(jié)點(diǎn)ID的管理方式以及改良種子運(yùn)算:
1.改良節(jié)點(diǎn)ID的管理方式
此策略的目的在于讓攻擊者無法輕易的算出IDattack,以及讓攻擊者在同一臺(tái)機(jī)器上(單一個(gè)IP)無法操縱多個(gè)不同節(jié)點(diǎn)ID。
作者建議,應(yīng)該嚴(yán)格要求同一臺(tái)機(jī)器(同一個(gè)IP)只能同時(shí)與一個(gè)節(jié)點(diǎn)ID相關(guān),也就是在table及db的儲(chǔ)存結(jié)構(gòu),以及nieghbor訊息的結(jié)構(gòu)中應(yīng)該去設(shè)定這個(gè)限制。此外,為了讓攻擊者無法輕易的算出IDattack,應(yīng)該要改變logdist函式的輸入?yún)?shù),而尋訪運(yùn)算也應(yīng)該改變其運(yùn)算目標(biāo)。
2. 改良種子運(yùn)算
作者建議,即使table不是空的,仍應(yīng)執(zhí)行種子運(yùn)算。此外,當(dāng)一個(gè)節(jié)點(diǎn)在重新啟動(dòng)之后,在被動(dòng)的執(zhí)行聯(lián)結(jié)更新運(yùn)算之前(由Unsolicited pings所觸發(fā)),應(yīng)優(yōu)先執(zhí)行尋訪運(yùn)算。如此以來,就可以盡量避免被攻擊用節(jié)點(diǎn)ID占據(jù)。
在geth 1.8.0版,作者所建議的改良種子運(yùn)算已經(jīng)實(shí)作。
C. Manipulating Time — 時(shí)間操作攻擊
這個(gè)攻擊利用了Ethereum防御重放攻擊的機(jī)制—若是某個(gè)UDP訊息的時(shí)間戳記比節(jié)點(diǎn)的本機(jī)時(shí)間還要早20秒以上,該訊息將被丟棄。
攻擊者借著操弄NTP等方式,使得受害者的本機(jī)時(shí)間總是比外界真實(shí)時(shí)間還要遲緩20秒以上(也就是比其他誠實(shí)節(jié)點(diǎn)遲緩20秒)。如此一來,受害者將不再理會(huì)其他誠實(shí)節(jié)點(diǎn)傳送過來的pong及neighbor訊息,而經(jīng)過數(shù)日之后,由于過期檢查機(jī)制,受害者的db將會(huì)清除掉所有的誠實(shí)節(jié)點(diǎn)。
另外,由于neighbor訊息都被丟棄,也會(huì)讓尋訪機(jī)制失去效用,導(dǎo)致table內(nèi)的誠實(shí)節(jié)點(diǎn)都被清除,最后就會(huì)使得受害者節(jié)點(diǎn)不再儲(chǔ)存任何誠實(shí)節(jié)點(diǎn)的資訊。而相對(duì)的,因?yàn)槭芎φ咭膊辉倮頃?huì)其他誠實(shí)節(jié)點(diǎn)傳送過來的ping及findnode訊息,在一段時(shí)間之后,其他誠實(shí)節(jié)點(diǎn)也就不會(huì)再儲(chǔ)存受害者節(jié)點(diǎn)的資訊。
依據(jù)以上的敘述,作者設(shè)計(jì)了一個(gè)實(shí)驗(yàn)。作者準(zhǔn)備了一臺(tái)已經(jīng)運(yùn)行34天的受害者節(jié)點(diǎn)(在紐約),接著便開始對(duì)受害者模擬NTP竄改時(shí)間攻擊(具體做法是直接改變受害者節(jié)點(diǎn)的本機(jī)時(shí)間),實(shí)驗(yàn)進(jìn)行6次,每一次受害者節(jié)點(diǎn)的本機(jī)時(shí)間都比真實(shí)時(shí)間延遲許多(6個(gè)不同的延遲時(shí)間:25秒,70秒,5分鐘,7分鐘,9分鐘,13分鐘),整個(gè)實(shí)驗(yàn)歷時(shí)數(shù)日(從8月17日至9月4日)。實(shí)驗(yàn)結(jié)果顯示,到了攻擊第3天,受害者db中紀(jì)錄的節(jié)點(diǎn)數(shù)量顯著的減少,而且之后便趨于平緩幾乎不再增加(從一開始的數(shù)萬個(gè)變成10幾個(gè)),而table中的數(shù)量也有一樣的變化趨勢(shì)。
雖然db及table中的節(jié)點(diǎn)數(shù)量大幅減少并且?guī)缀醪辉僭黾樱桥c受害者連接的節(jié)點(diǎn)數(shù)量卻呈現(xiàn)大幅波動(dòng),有時(shí)可以達(dá)到預(yù)設(shè)上限25個(gè),有時(shí)又不到10個(gè),進(jìn)一步分析之后發(fā)現(xiàn),這些與受害者連接的節(jié)點(diǎn),極大部分不屬于geth版本,而是諸如parity,Ethereum(J)等其他Ethereum的實(shí)作版本,在實(shí)驗(yàn)的最后11天里,作者統(tǒng)計(jì)了與受害者連結(jié)的節(jié)點(diǎn)數(shù)量與類別,其中屬于geth版本僅有130個(gè),而非geth版本的則高達(dá)64374個(gè)。
Manipulating Time攻擊能夠弱化受害者節(jié)點(diǎn),使得受害者幾乎無視其他節(jié)點(diǎn),也讓其他節(jié)點(diǎn)幾乎遺忘了受害者節(jié)點(diǎn),這樣一來,就能夠以更少的資源消耗來執(zhí)行Table Poisoning攻擊。
而針對(duì)Manipulating Time攻擊,作者建議應(yīng)該改良防御重放攻擊的方式,也就是每一個(gè)UDP消息不再以時(shí)間戳記(timestamp)進(jìn)行標(biāo)記,而是以隨機(jī)的nonce進(jìn)行標(biāo)記,當(dāng)送出ping或findnode訊息時(shí)附加一個(gè)nonce,而之后送回的pong或neighbor也應(yīng)該附加相對(duì)應(yīng)的nonce才視作合法訊息。
作者自己也坦承,這樣的改良只能保證pong或neighbor不會(huì)重放,卻無法保證ping或findnode本身重放。
以上便是波士頓大學(xué)團(tuán)隊(duì)研究所提出的幾種日蝕攻擊手法,在論文中,作者還有提出一些更深入的分析及實(shí)驗(yàn),例如,怎么樣去放大攻擊的規(guī)模,怎么樣做出最有效率(最省成本)的攻擊。
評(píng)論
查看更多