譯者:愛上平頂⼭
來源:慢霧區
原文鏈接:https://www.dasp.co/
這是分佈式應⽤安全項⽬(或DASP)2018 年排名前10的漏洞第⼀次迭代
該項⽬是NCC集團的⼀項舉措。這是⼀個開放的合作項⽬,致⼒於發現安全社區內的智能合約漏洞。要參與,請加⼊github⻚⾯。
1.重⼊漏洞
也被稱為 或與空⽩競爭,遞歸調⽤漏洞,未知調⽤
這種漏洞在很多時候被很多不同的⼈忽略:審閱者傾向於⼀次⼀個地審查函數,並且假定保護⼦例程的調⽤將安全並按預期運⾏。
Phil Daian
重⼊攻擊,可能是最着名的以太坊漏洞,第⼀次被發現時,每個⼈都感到驚訝。它在數百萬美元的搶劫案中⾸次亮相,導致了以太坊的分叉。當初始執⾏完成之前,外部合同調⽤被允許對調⽤合同進⾏新的調⽤時,就會發⽣重新進⼊。對於函數來說,這意味着合同狀態可能會在執⾏過程中因為調⽤不可信合同或使⽤具有外部地址的低級函數⽽發⽣變化。
損失:估計為350萬ETH(當時約為5000萬美元)
發現時間表:
2016年6⽉5⽇ Christian Reitwiessner發現了⼀個堅定的反模式
2016年6⽉9⽇ 更多以太坊攻擊:Race-To-Empty是真正的交易(vessenes.com)
2016年6⽉12⽇ 在以太坊智能合約’遞歸調⽤’錯誤發現(blog.slock.it)之後,沒有DAO資⾦⾯臨⻛險。
2016年6⽉17⽇ 我認為TheDAO現在正在流失(reddit.com)
2016年8⽉24⽇ DAO的歷史和經驗教訓(blog.sock.it)
真實世界影響:
示例:
- ⼀個聰明的合同跟蹤⼀些外部地址的平衡,並允許⽤戶通過其公共資⾦檢索
withdraw()
功能。 - ⼀個惡意的智能合同使⽤
withdraw()
函數檢索其全部餘額。 - 在更新惡意合同的餘額之前,受害者合同執⾏
call.value(amount)()
低級別函數將以太⽹發送給惡意合同。 - 該惡意合同有⼀個⽀付
fallback()
接受資⾦的功能,然後回調到受害者合同的withdraw()
功能。 - 第⼆次執⾏會觸發資⾦轉移:請記住,惡意合同的餘額尚未從⾸次提款中更新。結果, 惡意合同第⼆次成功退出了全部餘額。
代碼示例:
以下函數包含易受重⼊攻擊影響的函數。當低級別call()
函數向msg.sender
地址發送ether時,它變得易受攻擊; 如果地址是智能合約,則付款將觸發其備⽤功能以及剩餘的交易⽓體:
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount; }
其他資源:
2.訪問控制
通過調⽤initWallet函數,可以將Parity Wallet庫合約變為常規多sig錢包並成為它的所有者。
Parity
訪問控制問題在所有程序中都很常⻅,⽽不僅僅是智能合同。事實上,這是OWASP排名前10位的第5位。⼈們通常通過其公共或外部功能訪問合同的功能。儘管不安全的可視性設置會給攻擊者直接訪問合同的私有價值或邏輯的⽅式,但訪問控制旁路有時更加微妙。這些漏洞可能發⽣在合約使⽤已棄⽤tx.origin的驗證調⽤者時,⻓時間處理⼤型授權邏輯require並delegatecall在代理庫或代理合約中魯莽使⽤。
損失:估計為150,000 ETH(當時約3000萬美元)
真實世界影響:
示例:
- ⼀個聰明的合同指定它初始化它作為合同的業主的地址。這是授予特殊特權的常⻅模式,例如提取合同資⾦的能⼒。
- 不幸的是,初始化函數可以被任何⼈調⽤,即使它已經被調⽤。允許任何⼈成為合同的擁有者並獲得資⾦。
代碼示例:
在下⾯的例⼦中,契約的初始化函數將函數的調⽤者設置為它的所有者。然⽽,邏輯與合約的構造函數分離,並且不記錄它已經被調⽤的事實。
function initContract() public { owner = msg.sender; }
在Parity multi-sig錢包中,這個初始化函數與錢包本身分離並在“庫”合同中定義。⽤戶需要通過調⽤庫的函數來初始化⾃⼰的錢包delegateCall。不幸的是,在我們的例⼦中,函數沒有檢查錢包是否已經被初始化。更糟糕的是,由於圖書館是⼀個聰明的合同,任何⼈都可以⾃⾏初始化圖書館並要求銷毀。
其他資源:
3.算術問題
也被稱為 整數溢出和整數下溢
溢出情況會導致不正確的結果,特別是如果可能性未被預期,可能會影響程序的可靠性和安全性。
Jules Dourlens
整數溢出和下溢不是⼀類新的漏洞,但它們在智能合約中尤其危險,其中⽆符號整數很普遍,⼤多數開發⼈員習慣於簡單int類型(通常是有符號整數)。如果發⽣溢出,許多良性代碼路徑成為盜竊或拒絕服務的載體。
真實世界影響:
示例:
- ⼀個聰明的合同的
withdraw()
功能,您可以為您的餘額仍是⼿術后積極檢索,只要捐贈合同醚。 - ⼀個攻擊者試圖收回⽐他或她的當前餘額多。
- 該
withdraw()
功能檢查的結果總是正數,允許攻擊者退出超過允許。由此產⽣的餘額下降,並成為⽐它應該更⼤的數量級。
代碼示例:
最直接的例⼦是⼀個不檢查整數下溢的函數,允許您撤銷⽆限量的標記:
function withdraw(uint _amount) { require(balances[msg.sender] - _amount > 0); msg.sender.transfer(_amount); balances[msg.sender] -= _amount; }
第⼆個例⼦(在⽆益的Solidity編碼競賽期間被發現)是由於數組的⻓度由⽆符號整數表示的事實促成的錯誤的錯誤:
function popArrayOfThings() { require(arrayOfThings.length >= 0); arrayOfThings.length--; }
第三個例⼦是第⼀個例⼦的變體,其中兩個⽆符號整數的算術結果是⼀個⽆符號整數:
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) } }
第四個示例提供了即將棄⽤的var關鍵字。由於var將⾃身改變為包含指定值所需的最⼩類型,因此它將成為uint8保持值0.如果循環的迭代次數超過255次,它將永遠達不到該數字,並且在執⾏運⾏時停⽌出⽓:
for (var i = 0; i < somethingLarge; i ++) { // ... }
其他資源:
4.未檢查返回值的低級別調⽤
也稱為 或與靜默失敗發送,未經檢查發送
儘可能避免使⽤低級別的“調⽤”。如果返回值處理不當,它可能會導致意外的⾏為。Remix
其中的密實度的更深層次的特點是低級別的功能call()
,callcode()
,delegatecall()
和send()
。他們在計算錯誤⽅⾯的⾏為與其他Solidity函數完全不同,因為他們不會傳播(或冒泡),並且不會導致當前執⾏的全部回復。相反,他們會返回⼀個布爾值設置為false,並且代碼將繼續運⾏。這可能會讓開發⼈員感到意外,如果未檢查到這種低級別調⽤的返回值,則可能導致失敗打開和其他不想要的結果。請記住,發送可能會失敗!
真實世界影響:
代碼示例:
下⾯的代碼是⼀個當忘記檢查返回值時會出錯的例⼦send()。如果調⽤⽤於將ether發送給不接受它們的智能合約(例如,因為它沒有應付回退功能),則EVM將⽤其替換其返回值false。由於在我們的例⼦中沒有檢查返回值,因此函數對合同狀態的更改不會被恢復,並且etherLeft變量最終會跟蹤⼀個不正確的值:
function withdraw(uint256 _amount) public { require(balances[msg.sender] >= _amount); balances[msg.sender] -= _amount; etherLeft -= _amount; msg.sender.send(_amount); }
其他資源:
5.拒絕服務
包括達到gas上限,意外拋出,意外殺死,訪問控制違規
我不⼩⼼殺了它。
devops199 on the Parity multi-sig wallet
在以太坊的世界中,拒絕服務是致命的:儘管其他類型的應⽤程序最終可以恢復,但智能合同可以通過
其中⼀種攻擊永遠脫機。許多⽅⾯導致拒絕服務,包括在作為交易接受⽅時惡意⾏為,⼈為地增加計算
功能所需的⽓體,濫⽤訪問控制訪問智能合約的私⼈組件,利⽤混淆和疏忽,…這類攻擊包括許多不同的變體,並可能在未來⼏年看到很多發展。
損失:估計為514,874 ETH(當時約3億美元)
真實世界影響:
示例:
- ⼀個拍賣合同允許其⽤戶在競標不同的資產。
- 為了投標,⽤戶必須bid(uint object)⽤期望的以太數來調⽤函數。拍賣合同將把以太保存在第三⽅保存中,直到對象的所有者接受投標或初始投標⼈取消。這意味着拍賣合同必須在其餘額中保留未解決出價的全部價值。
- 該拍賣合同還包含⼀個withdraw(uint amount)功能,它允許管理員從合同獲取資⾦。隨着函數發送amount到硬編碼地址,開發⼈員決定公開該函數。
- ⼀個攻擊者看到了潛在的攻擊和調⽤功能,指揮所有的合同的資⾦為其管理員。這破壞了託管承諾並阻⽌了所有未決出價。
- 雖然管理員可能會將託管的錢退還給合同,但攻擊者可以通過簡單地撤回資⾦繼續進⾏攻擊。
代碼示例:
在下⾯的例⼦中(受以太王的啟發),遊戲合同的功能可以讓你成為總統,如果你公開賄賂前⼀個。不
幸的是,如果前總統是⼀個聰明的合同,並導致⽀付逆轉,權⼒的轉移將失敗,惡意智能合同將永遠保
持總統。聽起來像是對我的獨裁:
function becomePresident() payable { require(msg.value >= price); // must pay the price to become president president.transfer(price); // we pay the previous president president = msg.sender; // we crown the new president price = price * 2; // we double the price to become president }
在第⼆個例⼦中,調⽤者可以決定下⼀個函數調⽤將獎勵誰。由於for循環中有昂貴的指令,攻擊者可
能會引⼊太⼤的數字來迭代(由於以太坊中的⽓體阻塞限制),這將有效地阻⽌函數的功能。
function selectNextWinners(uint256 _largestWinner) { for(uint256 i = 0; i < largestWinner, i++) { // heavy code } largestWinner = _largestWinner; }
其他資源:
6.錯誤隨機性
也被稱為 沒有什麼是秘密的
合同對block.number年齡沒有⾜夠的驗證,導致400個ETH輸給⼀個未知的玩家,他在等待256個街區之前揭示了可預測的中獎號碼。
Arseny Reutov
以太坊的隨機性很難找到。雖然Solidity提供的功能和變量可以訪問明顯難以預測的值,但它們通常要麼⽐看起來更公開,要麼受到礦⼯影響。由於這些隨機性的來源在⼀定程度上是可預測的,所以惡意⽤戶通常可以複製它並依靠其不可預知性來攻擊該功能。
損失:超過400 ETH
真實世界影響:
示例:
- 甲智能合同使⽤塊號作為隨機有遊戲⽤的源。
- 攻擊者創建⼀個惡意合約來檢查當前的塊號碼是否是贏家。如果是這樣,它就稱為第⼀個智能合約以獲勝; 由於該呼叫將是同⼀交易的⼀部分,因此兩個合約中的塊編號將保持不變。
- 攻擊者只需要調⽤她的惡意合同,直到獲勝。
代碼示例:
在第⼀個例⼦中,a private seed與iteration數字和keccak256散列函數結合使⽤來確定主叫⽅是否獲勝。Eventhough的seed是private,它必須是通過交易在某個時間點設置,並因此在blockchain可⻅。
uint256 private seed; function play() public payable { require(msg.value >= 1 ether); iteration++; uint randomNumber = uint(keccak256(seed + iteration)); if (randomNumber % 2 == 0) { msg.sender.transfer(this.balance); } }
在這第⼆個例⼦中,block.blockhash正被⽤來⽣成⼀個隨機數。如果將該哈希值blockNumber設置為當前值block.number(出於顯⽽易⻅的原因)並且因此設置為,則該哈希值未知0。在blockNumber過去設置為超過256個塊的情況下,它將始終為零。最後,如果它被設置為⼀個以前的不太舊的區塊號碼,另⼀個智能合約可以訪問相同的號碼並將遊戲合同作為同⼀交易的⼀部分進⾏調⽤。
function play() public payable { require(msg.value >= 1 ether); if (block.blockhash(blockNumber) % 2 == 0) { msg.sender.transfer(this.balance); } }
其他資源:
– 在以太坊智能合約中預測隨機數
– 在以太坊隨機
7.前台運⾏
也被稱為 檢查時間與使⽤時間(TOCTOU),競爭條件,事務順序依賴性(TOD)
事實證明,只需要150⾏左右的Python就可以獲得⼀個正常運⾏的算法。
Ivan Bogatyy
由於礦⼯總是通過代表外部擁有地址(EOA)的代碼獲得燃⽓費⽤,因此⽤戶可以指定更⾼的費⽤以便
更快地開展交易。由於以太坊區塊鏈是公開的,每個⼈都可以看到其他⼈未決交易的內容。這意味着,
如果某個⽤戶正在揭示拼圖或其他有價值的秘密的解決⽅案,惡意⽤戶可以竊取解決⽅案並以較⾼的費
⽤複製其交易,以搶佔原始解決⽅案。如果智能合約的開發者不⼩⼼,這種情況會導致實際的和毀滅性
的前端攻擊。
真實世界影響:
示例:
- ⼀個聰明的合同發布的RSA號(N = prime1 x prime2)。
- 對其submitSolution()公共功能的調⽤與權利prime1並prime2獎勵來電者。
- Alice成功地將RSA編號考慮在內,並提交解決⽅案。
- 有⼈在⽹絡上看到愛麗絲的交易(包含解決⽅案)等待被開採,並以較⾼的天然⽓價格提交。
- 由於⽀付更⾼的費⽤,第⼆筆交易⾸先被礦⼯收回。該攻擊者贏得獎⾦。
其他資源:
8.時間篡改
也被稱為 時間戳依賴
如果⼀位礦⼯持有合同的股份,他可以通過為他正在挖掘的礦區選擇合適的時間戳來獲得優勢。
Nicola Atzei,Massimo Bartoletti和Tiziana Cimoli
從鎖定令牌銷售到在特定時間為遊戲解鎖資⾦,合同有時需要依賴當前時間。這通常通過Solidity中的
block.timestamp別名或其別名完成now。但是,這個價值從哪⾥來?來⾃礦⼯!由於交易的礦⼯在報告採礦發⽣的時間⽅⾯具有迴旋餘地,所以良好的智能合約將避免強烈依賴所宣傳的時間。請注意,
block.timestamp有時(錯誤)也會在隨機數的⽣成中使⽤,如#6所述。錯誤的隨機性。
真實世界影響:
示例:
- ⼀場⽐賽在今天午夜付出了第⼀名球員。
- 惡意礦⼯包括他或她試圖贏得⽐賽並將時間戳設置為午夜。
- 在午夜之前,礦⼯最終挖掘該塊。當前的實時時間“⾜夠接近”到午夜(當前為該塊設置的時間戳),⽹絡上的其他節點決定接受該塊。
代碼示例:
以下功能只接受特定⽇期之後的呼叫。由於礦⼯可以影響他們區塊的時間戳(在⼀定程度上),他們可以嘗試挖掘⼀個包含他們交易的區塊,並在未來設定⼀個區塊時間戳。如果⾜夠接近,它將在⽹絡上被接受,交易將在任何其他玩家試圖贏得⽐賽之前給予礦⼯以太:
function play() public { require(now > 1521763200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether); }
其他資源:
9.短地址攻擊
也被稱為 涉及⾮連鎖問題,客戶端漏洞
為令牌傳輸準備數據的服務假定⽤戶將輸⼊20字節⻓的地址,但實際上並未檢查地址的⻓度。
PawełBylica
短地址攻擊是EVM本身接受不正確填充參數的副作⽤。攻擊者可以通過使⽤專⻔製作的地址來利⽤這⼀點,使編碼錯誤的客戶端在將它們包含在事務中之前不正確地對參數進⾏編碼。這是EVM問題還是客戶問題?是否應該在智能合約中修復?儘管每個⼈都有不同的觀點,但事實是,這個問題可能會直接影響很多以太⽹。雖然這個漏洞還沒有被⼤規模利⽤,但它很好地證明了客戶和以太坊區塊鏈之間的交互帶來的問題。其他脫鏈問題存在:重要的是以太坊⽣態系統對特定的javascript前端,瀏覽器插件和公共節點的深度信任。臭名昭着的鏈外利⽤被⽤於Coindash ICO的⿊客在他們的⽹⻚上修改了公司的以太坊地址,誘騙參與者將ethers發送到攻擊者的地址。
發現時間表:
2017年4⽉6⽇ 如何通過閱讀區塊鏈來找到1000萬美元
真實世界影響:
示例:
- 交易所API具有交易功能,可以接收收件⼈地址和⾦額。
- 然後,API
transfer(address _to, uint256 _amount)
使⽤填充參數與智能合約函數進⾏交互:它將12位零字節的地址(預期的20字節⻓度)預先設置為32字節⻓ - 鮑勃()要求愛麗絲轉讓他20個代幣。他惡意地將她的地址截斷以消除尾隨的零。
0x3bdde1e9fbaef2579dd63e2abbf0be445ab93f00
- Alice使⽤交換API和Bob(
0x3bdde1e9fbaef2579dd63e2abbf0be445ab93f
)的較短的19字節地址。 - API⽤12個零字節填充地址,使其成為31個字節⽽不是32個字節。有效地竊取以下
_amount
參數中的⼀個字節。 - 最終,執⾏智能合約代碼的EVM將會注意到數據未被正確填充,並會在
_amount
參數末尾添加丟失的字節。有效地傳輸256倍以上的令牌。
其他資源:
10.未知的 未知物
我們相信更多的安全審計或更多的測試將沒有什麼區別。主要問題是評審⼈員不知道要尋找什麼。
Christoph Jentzsch
以太坊仍處於起步階段。⽤於開發智能合同的主要語⾔Solidity尚未達到穩定版本,⽽⽣態系統的⼯具仍處於試驗階段。⼀些最具破壞性的智能合約漏洞使每個⼈都感到驚訝,並且沒有理由相信不會有另⼀個同樣出乎意料或同樣具有破壞性的漏洞。只要投資者決定將⼤量資⾦⽤於複雜⽽輕微審計的代碼,我們將繼續看到新的發現導致可怕的後果。對智能合約進⾏正式驗證的⽅法尚不成熟,但它們似乎有望成為今⽇搖搖欲墜的現狀。隨着新類型的漏洞不斷被發現,開發⼈員需要繼續努⼒,並且需要開發新⼯具來在壞⼈之前找到它們。這個前10名可能會迅速發展,直到智能合約開發達到穩定和成熟的狀態。
转载请注明:IAMCOOL » 以太坊智能合約安全 Dasp Top10