黑客來(lái)襲,手把手帶你深挖區(qū)塊鏈安全漏洞區(qū)塊鏈
區(qū)塊鏈行業(yè)正面臨著私鑰生成與保護(hù)、共識(shí)過(guò)程中心化、智能合約代碼漏洞等安全問(wèn)題,開(kāi)發(fā)者該如何應(yīng)對(duì)及避免漏洞頻發(fā)呢?
目前,區(qū)塊鏈漏洞安全問(wèn)題頻發(fā)。
區(qū)塊鏈行業(yè)正面臨著私鑰生成與保護(hù)、共識(shí)過(guò)程中心化、智能合約代碼漏洞、簽名過(guò)程算法漏洞、系統(tǒng)實(shí)現(xiàn)代碼漏洞等安全問(wèn)題,對(duì)于以上問(wèn)題,開(kāi)發(fā)者該如何應(yīng)對(duì)及避免漏洞頻發(fā)呢?
以下為吳家志在CSDN主辦,區(qū)塊鏈大本營(yíng)、柏鏈道捷、極客幫創(chuàng)投協(xié)辦的技術(shù)沙龍上的發(fā)言內(nèi)容,區(qū)塊鏈大本營(yíng)在不改變?cè)獾那闆r下作了精心整理。
我先自我介紹一下,我叫吳家志,目前在PeckShield擔(dān)任研發(fā)副總。我們是今年年初轉(zhuǎn)到區(qū)塊鏈領(lǐng)域的,其實(shí)創(chuàng)業(yè)初期我們有做過(guò)一些其他的嘗試,之前我的工作比較偏向操作系統(tǒng)安全方面,主要在安卓方向,像360 C0RE Team、360超級(jí)ROOT都是我在 360期間開(kāi)發(fā)出來(lái)的產(chǎn)品。
目前,區(qū)塊鏈發(fā)展的趨勢(shì)是怎樣的?可以看一下數(shù)據(jù),藍(lán)線代表創(chuàng)建智能合約數(shù)量的趨勢(shì)。
可以看出,去年9月份,智能合約數(shù)量出現(xiàn)了大幅下滑,年底各種ICO的出現(xiàn),使智能合約數(shù)量有了很大的上升,今年2月份又出現(xiàn)了明顯的下降,過(guò)去的兩到三個(gè)月也都在下降,不過(guò)7月份開(kāi)始又往上漲了,這其中的原因,在場(chǎng)的大家都知道,我就不說(shuō)了。
而且,至少2017年下半年開(kāi)始到今年年初,由于區(qū)塊鏈各行業(yè)DApp不斷落地,行業(yè)形勢(shì)看起來(lái)一片大好。
區(qū)塊鏈這么火,投資人會(huì)投入大量資金去買幣、炒幣...
但在我們安全研究人員眼里來(lái)看,它其實(shí)是黑客非常感興趣的目標(biāo)。
從2014年的Mt.Gox的事件開(kāi)始,一直到今年這個(gè)時(shí)間,出現(xiàn)了很多區(qū)塊鏈安全事件。比如今年4月份有關(guān)智能合約漏洞的美鏈(BEC)事件,5月份的EDU,到近期的交易所被黑,各類安全事件很多很多。
這種安全問(wèn)題,它造成的結(jié)果是非常嚴(yán)重的,你的錢是真的不見(jiàn)了。不像是手機(jī)里面的照片、隱私被竊這種,這根本不是一個(gè)級(jí)別的。
今天我主要講兩個(gè)主題,智能合約安全和公鏈安全。
深入淺出:Fomo 3D漏洞
先講一下智能合約相關(guān)的安全問(wèn)題,近期F3D十分火爆,它有很多有趣的東西,就在6天前,我們就有一個(gè)比較新的研究……
微博:https://weibo.com/ttarticle/p/show?id=2309404265433178991610
這個(gè)故事是這樣的,大概7月 23日晚上 8點(diǎn)多,我在微博上發(fā)現(xiàn)了一條關(guān)于F3D的信息,Geth客戶端開(kāi)發(fā)團(tuán)隊(duì)的 team leader、以太坊核心成員 Peter 發(fā)現(xiàn)了一個(gè)F3D的airdrop()問(wèn)題,這引起了我的興趣,然后我們就去分析。
這個(gè)事情很有趣,先看右邊藍(lán)色框內(nèi),它就是F3D的智能合約之一,它其實(shí)有很多合約,今天我只講其中一個(gè)。
其中有一個(gè)環(huán)節(jié)做airdrop(),你可以理解為它利用了彩票抽獎(jiǎng)的機(jī)制,假設(shè)你投注F3D,不管你買了多少Key,買完之后它就有一個(gè)小概率事件,會(huì)讓你中獎(jiǎng),然后你就會(huì)有一個(gè)額外的激勵(lì)。我覺(jué)得這個(gè)設(shè)計(jì)也挺合理的,它可以活躍用戶參與。
0.1個(gè)ETH是F3D最低的獎(jiǎng)金,有人刻意用最低的投入去薅羊毛,他只要操作個(gè)錢,就有機(jī)會(huì)中獎(jiǎng)。最后智能合約里面的withdraw()函數(shù)把錢取出來(lái)。
先看一下function airdrop()這個(gè)函數(shù),有個(gè)名為seed的變量,seed是隨機(jī)數(shù)中很重要的一個(gè)概念,計(jì)算機(jī)世界中不存在一個(gè)絕對(duì)的隨機(jī)數(shù),你只能通過(guò)seed去生成一個(gè)相對(duì)的隨機(jī)數(shù)。
一旦你可以通過(guò)某種方法預(yù)測(cè)seed值,這個(gè)隨機(jī)其實(shí)就會(huì)從小概率事件可能變成一個(gè)大概率事件。
比如說(shuō)我剛開(kāi)始學(xué)編程的時(shí)候,一開(kāi)始學(xué)C,我用random()函數(shù)獲取隨機(jī)數(shù),我那時(shí)候還不知道seed,我就會(huì)傳空值或者傳零進(jìn)去,然后我發(fā)現(xiàn)每次跑都會(huì)是同一個(gè)結(jié)果。
這個(gè)黑客找到了一個(gè)方式,在每次投注的時(shí)候,預(yù)測(cè)說(shuō)這里一定會(huì)開(kāi)獎(jiǎng),百分之百會(huì)開(kāi)獎(jiǎng)。確定之后他再投這個(gè)錢,所以他每次都可以從里面拿錢出來(lái)。
說(shuō)一下這個(gè)東西是怎么被預(yù)測(cè)的?其實(shí)它的實(shí)現(xiàn)是這樣的,可以看到seed運(yùn)算里面有這么幾個(gè)關(guān)鍵點(diǎn),比如說(shuō)timestamp、difficulty、coinbase、gaslimit和number。
還有一個(gè)比較特別的,就是airDropTracker_,事實(shí)上這些都是合約里面的一個(gè)值,你可以隨時(shí)把它讀出來(lái),所以你可以認(rèn)為前面提到的全部都是已知的,唯一你不能預(yù)測(cè)的,就是msg.sender。
當(dāng)時(shí)Peter認(rèn)為F3D團(tuán)隊(duì)假設(shè)認(rèn)為msg.sender的隨機(jī)性是足夠的,其實(shí) msg.sender就是你進(jìn)到合約時(shí)的錢包地址,F(xiàn)3D團(tuán)隊(duì)認(rèn)為你不可能生成大量的錢包去嘗試,然后讓seed變成可預(yù)測(cè)的,但并不是只能從錢包進(jìn)來(lái),也可以從另外一個(gè)合約進(jìn)來(lái),這個(gè)其實(shí)就是關(guān)鍵。
這就又涉及到一個(gè)算法,有一個(gè)叫isHuman()的函數(shù)判斷,判斷是真人錢包還是其他合約,但問(wèn)題是這個(gè)判斷有一個(gè)錯(cuò)誤,就是Extcodesize()。
你可以用智能合約不停地刷airdrop(),這很像一個(gè)挖礦的過(guò)程,因?yàn)橹悄芎霞s生成的新合約地址是可預(yù)測(cè)的,所以你就可以一直不停的算,直到算出一個(gè)seed結(jié)果滿足return(true) 時(shí),就可以保證開(kāi)獎(jiǎng)。
一旦智能合約在constructor()里面去調(diào)isHuman()判斷時(shí),它就會(huì)誤判,然后造成人為開(kāi)獎(jiǎng)的結(jié)果。
所以,就會(huì)出現(xiàn)這樣一個(gè)的攻擊模式——首先可以生成多個(gè)合約,然后就一直算,直到算到某一個(gè)可以搭配當(dāng)下airDropTracker_的合約X時(shí),就可以保證開(kāi)獎(jiǎng),然后再根據(jù)合約X去投注,就可以不斷把財(cái)經(jīng)池里面的財(cái)經(jīng)全部都拿走。
但其實(shí)對(duì)F3D來(lái)說(shuō),這也不是一個(gè)太大的問(wèn)題。因?yàn)?span >被攻擊的財(cái)經(jīng)池只是F3D整個(gè)獎(jiǎng)金環(huán)節(jié)中的很小一塊。
所以,它就好比你玩某一個(gè)游戲,有一個(gè)外掛,你的攻擊力每次都加9,保證你可以把人砍死,大概是這樣,有點(diǎn)類似破壞游戲平衡吧,但其實(shí)對(duì)整個(gè)游戲的影響不會(huì)太大,比如說(shuō)把所有人的個(gè)人資產(chǎn)都拿走。
其他智能合約相關(guān)漏洞
接下來(lái)講一些其他有關(guān)智能合約安全的問(wèn)題。
有一類問(wèn)題是跟allowance有關(guān)的,比如ERC20里面有一個(gè)標(biāo)準(zhǔn)的API叫transferFrom(),它允許某一個(gè)人把你的錢轉(zhuǎn)走,但前提是事先已經(jīng)聲明好的,在哪里去聲明?就是在allowed[]這個(gè)數(shù)組里。
這里有一個(gè)問(wèn)題,雖然它可以允許你把錢轉(zhuǎn)走,但是卻無(wú)法判斷你要轉(zhuǎn)走多少錢,它其實(shí)是僅僅把這個(gè)allowance剪掉了,這是代碼上的一個(gè)bug,EDU問(wèn)題就屬于這種類型。
這是我做的一個(gè)實(shí)驗(yàn),我隱去了一些信息。
最上面那個(gè)0xd開(kāi)頭的值是我的一個(gè)錢包地址,然后To指向是受我攻擊的合約的地址,攻擊完成后,我把0xa地址其中的一個(gè)EDU,就是好多0后面有1個(gè)的那個(gè),轉(zhuǎn)到了0x6這個(gè)地址,其實(shí)0x6也是我的錢包地址。
Input Data這一塊是一些攻擊的細(xì)節(jié),其實(shí)這個(gè)構(gòu)造起來(lái)非常簡(jiǎn)單,你只要去調(diào)transferFrom()函數(shù),然后你就傳入from、to和value。所以,我偷了一個(gè)EDU到我的錢包,這個(gè)攻擊就是這樣的。
接下來(lái),以batchTransfer()為例,講一下overflow相關(guān)的漏洞,它的攻擊是這樣完成的。
在調(diào)用batchTransfer()函數(shù)時(shí),有兩個(gè)可以傳入的參數(shù),一個(gè)是receivers,一個(gè)是value。
receivers是一個(gè)數(shù)組,它傳入了40這個(gè)數(shù),這個(gè)數(shù)值表示偏移量,是需要傳給智能合約的。然后下面還有2或者8開(kāi)頭的。
這個(gè)邏輯是這樣的,在batchTransfer()的實(shí)現(xiàn)上面,就是把8開(kāi)頭后面全部都是0的這個(gè)值,同時(shí)轉(zhuǎn)給兩個(gè)人
如果按一般的邏輯去理解,賬號(hào)里必須有足夠的錢才能夠轉(zhuǎn)過(guò)去。但問(wèn)題是,在它實(shí)現(xiàn)檢查時(shí),它的實(shí)現(xiàn)方法是直接算兩個(gè)數(shù)的乘積。
用0去乘,結(jié)果是很明顯的,就算用最大的字節(jié)去乘,乘完之后它也會(huì)變成0。
所以,你可以在沒(méi)有任何錢的情況下把這么多錢轉(zhuǎn)給那兩個(gè)人,也就是上面紅色框部分,你會(huì)看到這個(gè)地方兩個(gè)transfer都是這么大量的Token,美鏈?zhǔn)录褪沁@樣,通過(guò)這種方式可以無(wú)中生有造出大量的假幣,然后再把假幣充到OKEx里面,然后就可以進(jìn)行砸盤等各種操作。
另外一個(gè)案例和transfer還蠻像的,但稍微復(fù)雜一點(diǎn),兩個(gè)address跟value都是數(shù)組。
它們溢出的方式一樣,就是說(shuō)把這些值加起來(lái)或者乘起來(lái),然后造成的結(jié)果也是一樣的,就是很大量的假幣就充進(jìn)你錢包地址。
到這里,我大概簡(jiǎn)單講了講智能合約方面的安全問(wèn)題,其中一個(gè)是F3D薅羊毛的最新研究,還有一些過(guò)去我們發(fā)現(xiàn)的其他問(wèn)題。
公鏈安全
今天的第二個(gè)主題,我講一下關(guān)于Infrastructure方面的安全問(wèn)題,就是所謂的公鏈安全。
PeckShield在以太坊公鏈上做了很多研究,EOS相關(guān)的也有一些。
講到公鏈問(wèn)題其實(shí)你可以這樣理解,比如說(shuō)你用一個(gè)360手機(jī)衛(wèi)士,可以理解為有一個(gè)客戶端在手機(jī)上運(yùn)行,然后還有各種服務(wù)端,你要發(fā)送各種請(qǐng)求,然后它會(huì)推送一些信息給你。
而在區(qū)塊鏈里面,你可以理解為只有客戶端沒(méi)有服務(wù)端,至少以太坊上是這樣子的,每一個(gè)節(jié)點(diǎn)基本上長(zhǎng)的都是一模一樣的。
所以,那時(shí)候我們開(kāi)始進(jìn)入?yún)^(qū)塊鏈行業(yè)、研究主鏈安全時(shí),我們其實(shí)就是去看所有客戶端的代碼,看它實(shí)現(xiàn)的邏輯有沒(méi)有什么錯(cuò)誤。
這邊是一個(gè)統(tǒng)計(jì),Ethereum Nodes這個(gè)網(wǎng)站可以統(tǒng)計(jì)以太坊上都運(yùn)行了哪些客戶端,比如說(shuō)有Geth、C 和Parity,還有Python。所以,用的比較多的是Geth和Parity,今天的研究主要在Geth,因?yàn)樗枪俜降那矣脩袅看螅瑢?duì)Parity也有一些研究。
首先講Geth這一塊,其實(shí)談到任何的安全問(wèn)題,我們都需要先理解這樣一個(gè)事情,就比如說(shuō)Geth是一個(gè)盒子,我們用什么方式可以攻破它?就是所謂的Attack Surface(攻擊面)的問(wèn)題。
比如說(shuō)可以通過(guò)一個(gè)智能合約去攻擊它,那它可能就是EVM方面的問(wèn)題。
還可以通過(guò)一個(gè)RPC接口,它就可以類比成以前那種Web安全相關(guān)的這種問(wèn)題。
還有一種,通過(guò)協(xié)議棧攻擊,這兩個(gè)其實(shí)是有點(diǎn)不一樣的。因?yàn)閰^(qū)塊鏈里面的每一個(gè)客戶端之間,它們會(huì)互相去同步,或者互相分享信息,所以協(xié)議棧的問(wèn)題是最底層、也比較關(guān)鍵的問(wèn)題,一旦出問(wèn)題的話,整個(gè)系統(tǒng)可能就不運(yùn)行了。所以,接下來(lái)我主要在協(xié)議棧這一方面進(jìn)行一些探討。
這是以太坊的協(xié)議棧,大概是這樣,很多部分可能你們以前沒(méi)有聽(tīng)到,像ETH、LES和Whisper是你們比較熟知的,就是以太坊的協(xié)議。然后跟它并列的還有一些其他的,它的底層其實(shí)是包含RLPx、DΞVp2p的。
假設(shè)說(shuō)你運(yùn)行過(guò)Parity或者Geth,你要同步一個(gè)full node其,實(shí)很花時(shí)間,我那時(shí)候剛開(kāi)始弄花了可能有小一個(gè)月,就看你的帶寬怎么樣了;還有就是硬盤,我發(fā)現(xiàn)機(jī)械式硬盤基本沒(méi)戲,一定要SSD才有可能進(jìn)行同步。
與ETH并列的協(xié)議LES,它是一個(gè)比較輕的協(xié)議,你在同步時(shí),你不需要將所有的信息都同步,你可能只會(huì)同步一些metadata。
所以,在運(yùn)行Geth時(shí),你可以去指定運(yùn)行LES mode這種模式。
接下來(lái),詳細(xì)介紹一下我們之前發(fā)現(xiàn)的關(guān)于LES的安全問(wèn)題。
簡(jiǎn)單的說(shuō),就是一個(gè)越界讀的問(wèn)題,問(wèn)題出在上面這一行代碼,就在query.Skip這里。
我們可以自己去寫一個(gè)類似于Geth的客戶端,我只要知道你Geth的IP就ok。比如說(shuō)有一個(gè)礦池,礦池總要有一個(gè)節(jié)點(diǎn)去同步到鏈上,我一旦知道這個(gè)IP是什么,我就可以偽造這個(gè)東西,然后讓你崩潰。
那么,如何讓它崩潰的?我把Skip值設(shè)成-1(在協(xié)議棧上你可以自由設(shè)置),-1 1就是0,就等于說(shuō)我可以嘗試讓你去分配一個(gè)長(zhǎng)度為0的數(shù)組,然后我又可以去讀這個(gè)數(shù)組的Skip值為-1的位置,那一定不再是你的位置了,它已經(jīng)超過(guò)了你可以控制的內(nèi)存范圍,所以它就必然會(huì)崩潰。
接下來(lái),演示一下這個(gè)EPoD Demo,問(wèn)題出現(xiàn)在1810版本之前,也包括1810這個(gè)版本。
在拍攝視頻時(shí),我用的是官網(wǎng)上面的1810版本,對(duì)比過(guò)MD5,它是沒(méi)有經(jīng)過(guò)任何修改的;還需要處于離線環(huán)境,避免遭受外界攻擊,保證所受攻擊都來(lái)自本機(jī);運(yùn)行Geth的LES mode。
然后監(jiān)聽(tīng)從本機(jī)發(fā)出來(lái)的30303端口,在運(yùn)行攻擊代碼后, EXP后面的地址(即本機(jī)地址)是127.0.0.1,端口是30303,此時(shí)的Geth差不多就已經(jīng)崩了。
index out of range,崩的理由就是這樣,一個(gè)越界讀。它在處理某一個(gè)這種LESMessages的時(shí)候,造成了一個(gè)越界讀。TCpdump就是我攻擊的錢包,我們把這個(gè)漏洞叫EPoD,我只要知道你的IP,發(fā)一個(gè)包,Geth就會(huì)崩潰,大概就是這樣。
還有一些攻擊,我應(yīng)該會(huì)在9月份舉辦的ISC上講到。
比如說(shuō)你新成立一個(gè)X交易所,然后有一個(gè)成立很久的Y交易所,你覺(jué)得它的交易量很大,想讓它變小,你就可以去攻擊Y交易所。
一旦你知道了Y交易所的IP以及它同步的node,就可以把信息擴(kuò)散出去,就會(huì)忙死一片了。
像剛剛講的礦池,礦池最基本的一個(gè)事情就是算力,每個(gè)礦池之間都在競(jìng)爭(zhēng)算力,那我一旦可以讓某些礦池癱瘓,這個(gè)礦池后面的算力也就沒(méi)了,這也是非常嚴(yán)重的。
但是通常我知道,礦池在使用Geth的同時(shí),也會(huì)使用Parity,兩個(gè)客戶端會(huì)實(shí)時(shí)進(jìn)行同步。一旦我們發(fā)現(xiàn)Geth和Parity同時(shí)存在問(wèn)題的時(shí)候,就十分有趣了。
還有就是所謂的Boot Node,這個(gè)玩過(guò)的人可能會(huì)知道,一開(kāi)始同步的時(shí)候你需要知道從哪邊開(kāi)始同步,從哪邊開(kāi)始就是所謂的Boot Node。
所以,當(dāng)你癱瘓掉以太坊所有的Boot Node時(shí),新的Geth就沒(méi)有辦法去同步,因?yàn)樗緵](méi)有辦法啟動(dòng)。
一旦找到這種涉及公鏈安全而且又是協(xié)議棧的問(wèn)題,就可能會(huì)遭受這些攻擊的影響。
所以,如果Geth低于1810版本就趕緊升級(jí)吧。
1.TMT觀察網(wǎng)遵循行業(yè)規(guī)范,任何轉(zhuǎn)載的稿件都會(huì)明確標(biāo)注作者和來(lái)源;
2.TMT觀察網(wǎng)的原創(chuàng)文章,請(qǐng)轉(zhuǎn)載時(shí)務(wù)必注明文章作者和"來(lái)源:TMT觀察網(wǎng)",不尊重原創(chuàng)的行為TMT觀察網(wǎng)或?qū)⒆肪控?zé)任;
3.作者投稿可能會(huì)經(jīng)TMT觀察網(wǎng)編輯修改或補(bǔ)充。