這兩天幣圈鏈圈被美鏈BEC智能合約的漏洞導(dǎo)致代幣價(jià)值幾乎歸零的事件刷遍朋友圈演熟。這篇文章就來(lái)分析下BEC智能合約的漏洞谭网。
漏洞攻擊交易
我們先來(lái)還原下攻擊交易裕坊,這個(gè)交易可以在這個(gè)鏈接查詢到滋饲。
我截圖給大家看一下:
攻擊者向兩個(gè)賬號(hào)轉(zhuǎn)移57896044618…000.792003956564819968
個(gè)BEC桶现,相當(dāng)于BEC憑空進(jìn)行了一個(gè)巨大的增發(fā)躲雅,幾乎導(dǎo)致BEC價(jià)格瞬間歸零。
下面我們來(lái)分析下這個(gè)攻擊過(guò)程骡和。
合約漏洞分析
我們先來(lái)看看BEC智能合約的代碼相赁,
BEC在合約中加入一個(gè)批量轉(zhuǎn)賬的函數(shù),它的實(shí)現(xiàn)如下:
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value;
require(cnt > 0 && cnt <= 20);
require(_value > 0 && balances[msg.sender] >= amount);
balances[msg.sender] = balances[msg.sender].sub(amount);
for (uint i = 0; i < cnt; i++) {
balances[_receivers[i]] = balances[_receivers[i]].add(_value);
Transfer(msg.sender, _receivers[i], _value);
}
return true;
這個(gè)函數(shù)的作用是慰于,調(diào)用者傳入若干個(gè)地址和轉(zhuǎn)賬金額钮科,在經(jīng)過(guò)一些條件檢查之后,對(duì)msg.sender
的余額進(jìn)行減操作婆赠,對(duì)每一個(gè)傳入的地址進(jìn)行加操作绵脯,以實(shí)現(xiàn)BEC的轉(zhuǎn)移。
問(wèn)題出在 uint256 amount = uint256(cnt) * _value;
這句代碼,當(dāng)傳入值_value
過(guò)大時(shí)(接近uint256
的取值范圍的最大值)蛆挫,uint256 amount = uint256(cnt) * _value
計(jì)算時(shí)會(huì)發(fā)生溢出赃承,導(dǎo)致amount
實(shí)際的值是一個(gè)非常小的數(shù)(此時(shí)amount
不再是cnt * _value
的實(shí)際值),amount
很小悴侵,也使得后面對(duì)調(diào)用者余額校驗(yàn)可正常通過(guò)(即require(_value > 0 && balances[msg.sender] >= amount)
語(yǔ)句通過(guò))瞧剖。
我們來(lái)結(jié)合實(shí)際攻擊交易使用的參數(shù)來(lái)分析一下:
batchTransfer
的參數(shù)_value
值為16進(jìn)制的800000000000000000000...
,參數(shù)_receivers
數(shù)組的大小為2
可免,相乘之后剛好可超過(guò)uint256
所能表示的整數(shù)大小上限抓于,引發(fā)溢出問(wèn)題amount
實(shí)際的值為0
,后面的轉(zhuǎn)賬操作實(shí)際上msg.sender
的余額減0
巴元, 而對(duì)兩個(gè)賬號(hào)進(jìn)行了加16進(jìn)制的800000000000000000000...
毡咏,最終的結(jié)果是相當(dāng)于增發(fā)了2 * 16進(jìn)制的800000000000000000000...
驮宴。
實(shí)際上對(duì)于這種整數(shù)溢出漏洞逮刨,最簡(jiǎn)單的方法是采用 SafeMath
數(shù)學(xué)計(jì)算庫(kù)來(lái)避免。有趣的是BEC智能合約代碼中堵泽,其實(shí)其他的都使用了SafeMath
修己, 而關(guān)鍵的uint256 amount = uint256(cnt) * _value
卻沒(méi)有使用。
心痛程序員迎罗,也心痛韭菜睬愤。這句代碼改為uint256 amount = _value.mul(uint256(cnt));
就可以防止溢出問(wèn)題。
所以在做加減乘除的時(shí)候請(qǐng)記得一定使用:SafeMath
纹安,代碼在這里
原文:美鏈BEC合約漏洞技術(shù)分析
作者:Tiny熊