一夜回到解放前?溢出漏洞類型全面分析區(qū)塊鏈
溢出漏洞類型主要分為乘法溢出、加法溢出、減法溢出。
4月發(fā)生的BEC事件以及SMT事件已經(jīng)沉淀一段時(shí)間了,具體的情況也被多方媒體所報(bào)道,相關(guān)的漏洞根源問題也有很多大神團(tuán)隊(duì)的分析和指正,成都鏈安科技團(tuán)隊(duì)將各種已經(jīng)發(fā)生或可能發(fā)生的類似溢出漏洞原理進(jìn)行整理,再次將全方位的原理分析與大家分享。
事件回顧
2018年4月22日,黑客對BEC智能合約發(fā)起攻擊,憑空取出57,896,044,618,658,100,000,000,000,000,000,000,000,000,000,000,000,000,000,000.792003956564819968 個(gè)BEC代幣并在市場上進(jìn)行拋售,BEC隨即急劇貶值,價(jià)值幾乎為0,該市場瞬間土崩瓦解[1]。
2018年4月25日,SMT項(xiàng)目方發(fā)現(xiàn)其交易存在異常,黑客利用其函數(shù)漏洞創(chuàng)造了65,133,050,195,990,400,000,000,000,000,000,000,000,000,000,000,000,000,000,000
50,659,039,041,325,800,000,000,000,000,000,000,000,000,000,000,000,000,000,000
的SMT幣,火幣Pro隨即暫停了所有幣種的充值提取業(yè)務(wù)[2]。
讓我們一起以沉痛的心情緬懷以上一夜歸零的代幣
僅僅在BEC事件過去后的12小時(shí)內(nèi),就有多達(dá)12個(gè)項(xiàng)目的智能合約存在類似的整數(shù)溢出類型的漏洞,黑客可以利用這一漏洞轉(zhuǎn)賬“無中生有”的巨大數(shù)量代幣,也就是我們所說的“增發(fā)”。
代幣增發(fā)為什么就會(huì)貶值
人們常說“物以稀為貴”,意思是事物因稀少而且有益,從而顯得珍貴[3]。
黃金被選為最早時(shí)期全世界公認(rèn)的貨幣的原因就是因?yàn)樗∪?,地球上?chǔ)量有限,易分割,屬性穩(wěn)定等特點(diǎn)[4]。
中國人首先發(fā)明了“交子”,來代替不便交易的黃金。
所以就出現(xiàn)了金本位的概念,很多國家發(fā)行自己貨幣的時(shí)候都是按照其黃金儲(chǔ)量來發(fā)行,并不是想發(fā)多少就發(fā)多少。
試想一下如果憑空出現(xiàn)了一顆黃金做的大隕石,撞擊地球后黃金撒了一地,黃金還會(huì)值錢嗎?
所以當(dāng)一個(gè)代幣的數(shù)量在有了額定發(fā)行總量之后,突然肆意的增加,必然會(huì)導(dǎo)致代幣的貶值,甚至失去在市場上流通的意義。這就是“代幣增發(fā)”的不良后果。
整數(shù)溢出漏洞分析
前面提到,黑客是利用整數(shù)溢出漏洞繞過了轉(zhuǎn)賬數(shù)額的相關(guān)規(guī)則,進(jìn)而增發(fā)代幣。
那什么是整數(shù)溢出呢?為什么能用整數(shù)溢出來實(shí)現(xiàn)增發(fā)來秀一波空手套白狼的操作?
我來舉個(gè)例子
比如有一個(gè)國家的人不會(huì)復(fù)雜的數(shù)學(xué)運(yùn)算,只會(huì)從0數(shù)到9,每次數(shù)到9之后又從0開始數(shù),最后以數(shù)到的數(shù)作為結(jié)果。大家都這樣平安無事的生活著,但有一天,有一個(gè)從別的國家來的小黑,他發(fā)現(xiàn)了這個(gè)問題,于是他去金庫拿金錠,拿出來的個(gè)數(shù)超過了9個(gè),于是金庫管理員幫他數(shù),數(shù)到9之后又從0開始了,最后結(jié)算發(fā)現(xiàn)他取出來的結(jié)果是0個(gè)金錠,但實(shí)際上他已經(jīng)把金庫里的金錠幾乎都取完了。
黑客利用類似的機(jī)制憑空向一個(gè)賬戶中轉(zhuǎn)賬了超級(jí)大數(shù)額的代幣,而合約中的邏輯只要求他花費(fèi)很小的代價(jià)。
成都鏈安科技審計(jì)組對過往代幣增發(fā)事件漏洞類型進(jìn)行整理,概括出整型溢出類型漏洞的全面分析。
以太坊虛擬機(jī)(EVM)為整數(shù)指定固定大小的數(shù)據(jù)類型。這意味著一個(gè)整型變量只能有一定范圍的數(shù)字表示。例如,一個(gè) uint8 ,只能存儲(chǔ)在范圍 [0,255] 的數(shù)字。試圖存儲(chǔ) 256 到一個(gè) uint8 將變成 0。不加注意的話,只要沒有檢查用戶輸入又執(zhí)行計(jì)算,導(dǎo)致數(shù)字超出存儲(chǔ)它們的數(shù)據(jù)類型允許的范圍,Solidity 中的變量就可以被用來組織攻擊。
整數(shù)溢出的類型包括乘法溢出,加法溢出,減法溢出三種,
鏈安科技團(tuán)隊(duì)對BEC事件進(jìn)行分析后,發(fā)現(xiàn)可將其漏洞歸類于乘法溢出,原理如下:
乘法溢出
案例(CVE-2018-10299)
上述合約代碼中,存在漏洞的代碼為uint256 amount = uint256(cnt) * _value;,計(jì)算轉(zhuǎn)出總額度amount未使用SafeMath也未對溢出進(jìn)行檢查,直接將轉(zhuǎn)賬地址數(shù)量乘以轉(zhuǎn)賬額度,如果輸入極大的_value,那么amount計(jì)算結(jié)果就可能產(chǎn)生溢出,導(dǎo)致代幣增發(fā)。
在Remix-ide中測試如下:
1、部署合約;
2、調(diào)用batchTransfer函數(shù),向batchTransfer函數(shù)傳入地址數(shù)組["0xb4D30Cac5124b46C2Df0CF3e3e1Be05f42119033","0x0e823fFE018727585EaF5Bc769Fa80472F76C3d7"],以及_value"0x8000000000000000000000000000000000000000000000000000000000000000"即2*255,使得amount=2\*255 * 2,超出uint256類型的范圍[0,2**256-1],溢出為0,發(fā)送者賬戶余額不減少,并且,本例中,發(fā)送者的代幣可以為零,實(shí)現(xiàn)"無中生有"。
3、查看余額:
而針對SMT事件進(jìn)行分析后,發(fā)現(xiàn)其漏洞屬于加法溢出類型,其原理如下:
加法溢出
案例
上述合約代碼中,mintToken函數(shù)的功能是owner向指定賬戶增發(fā)mintedAmount數(shù)量的代幣,但是在對balanceOf[target]與totalSupply進(jìn)行加法操作未做溢出檢查,導(dǎo)致其可能存在溢出,并且,通過溢出,惡意owner可以任意增減target賬戶的余額,或者增發(fā)實(shí)際遠(yuǎn)遠(yuǎn)超過totalSupply的代幣。
在Remix-ide中測試如下:
1、部署合約;
2、向target預(yù)先轉(zhuǎn)一部分代幣,模擬目標(biāo)賬戶中已有的代幣:調(diào)用transfer函數(shù),傳入target地址:
0x14723a09acff6d2a60dcdf7aa4aff308fddc160c,以及轉(zhuǎn)賬額度,比如2000000000000000000(2 * 10**uint256(decimals));
3、如果owner想控制target的余額減半,那么,他只需要向target增發(fā)2*256-balanceOf[target] 10\*18=0xfffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c0000,現(xiàn)在調(diào)用mintToken函數(shù),向target地址轉(zhuǎn)入上述數(shù)量的代幣:
4、查詢target余額:
另外還有減法溢出的操作,雖然未出現(xiàn)相關(guān)的漏洞攻擊事件,我們也提供了相關(guān)的原理分析:
減法溢出
案例
上述合約代碼中,distribute函數(shù)的功能是從owner賬戶向指定的地址列表轉(zhuǎn)入2000 * 10**8代幣,但是在對balances[owner]的計(jì)算中未使用SafeMath,也未判斷owner賬戶是否有足夠的代幣,當(dāng)轉(zhuǎn)出代幣總量大于owner賬戶余額的時(shí)候,balances[owner]產(chǎn)生減法溢出,變成一個(gè)極大值。
在Remix-ide中測試如下:
部署合約
調(diào)用distribute函數(shù),傳入地址數(shù)組:
["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"]
owner分別向這兩個(gè)地址發(fā)送2000 * 10**8代幣,超過owner余額,balances[owner]產(chǎn)生減法溢出;
查詢owner賬戶余額,等于2**256-2000*10**8:
黑客雖然無孔不入,但鏈安科技團(tuán)隊(duì)對具有此類漏洞的合約進(jìn)行綜合性調(diào)研后,提出了“亡羊補(bǔ)牢”的修復(fù)措施:
漏洞修復(fù)
OpenZeppelin提供了一套很好的SafeMath庫,使用SafeMath庫函數(shù)能夠有效避免溢出漏洞,SafeMath庫源碼如下:
子曰:吾日三省吾身
我們從這些慘痛的教訓(xùn)中能總結(jié)出:
基于ERC20協(xié)議編寫的這些智能合約給予開發(fā)者的權(quán)力過大了,這些溢出問題本應(yīng)該放在底層檢測。
編寫合約的開發(fā)者沒有以嚴(yán)謹(jǐn)敬業(yè)的精神去遵守開發(fā)規(guī)范,使用SafeMath去做相關(guān)功能,或者進(jìn)行溢出測試,在這里提出批評。
沒有嚴(yán)謹(jǐn)?shù)倪壿嫞瑔螁螒{借創(chuàng)造力并不是都能出成果,還是要穩(wěn)中求勝,能使用庫就使用庫。
所以,鏈安科技團(tuán)隊(duì)建議,為了避免程序結(jié)果中產(chǎn)生溢出,破壞智能合約執(zhí)行邏輯,建議開發(fā)者在所有數(shù)學(xué)運(yùn)算中都使用SafeMath(敲黑板)。
引用:
[1]:http://bc.jrj.com.cn/2018/04/26132824455908.shtml
[2]: https://zhuanlan.zhihu.com/p/36116810
[3]: https://baike.baidu.com/item/物以稀為貴
[4]: http://www.woshipm.com/blockchain/945232.html
1.TMT觀察網(wǎng)遵循行業(yè)規(guī)范,任何轉(zhuǎn)載的稿件都會(huì)明確標(biāo)注作者和來源;
2.TMT觀察網(wǎng)的原創(chuàng)文章,請轉(zhuǎn)載時(shí)務(wù)必注明文章作者和"來源:TMT觀察網(wǎng)",不尊重原創(chuàng)的行為TMT觀察網(wǎng)或?qū)⒆肪控?zé)任;
3.作者投稿可能會(huì)經(jīng)TMT觀察網(wǎng)編輯修改或補(bǔ)充。