1. 程式人生 > >三天竟然爆發兩起大漏洞事件!我們來教你如何跳過以太坊的坑

三天竟然爆發兩起大漏洞事件!我們來教你如何跳過以太坊的坑

“現在進入你還是先行者,最後觀望者進場才是韭菜。”美圖董事長蔡文勝在三點鐘群中的預言一語成讖。在4月22日,隨著BEC智慧合約漏洞的爆出,一行程式碼蒸發了6447277680人民幣。然而時隔三天,SMT的智慧合約又爆出漏洞,SMT在火幣Pro的價格下跌近20%。一時間,無論先行者還是準“韭菜”,都慘遭收割。

區塊鏈做為一款能與價值互動的產品,難免不被人們神化。理性地分析一下,程式中的漏洞總是不可避免的,很難保證程式碼百分百不出錯,即使大公司也只能通過釋出測試版本來降低漏洞出現的概率。今天讓我們來看看智慧合約的初創者——以太坊智慧合約都有什麼“坑”,並且怎麼寫程式碼才不被坑。

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

作者 | ConsenSys Diligence

譯者 | Guoxi

4月25日上午,火幣Pro釋出公告,虛擬幣SMT專案方反饋25日凌晨發現其交易存在異常問題,經初步排查,SMT的以太坊智慧合約存在漏洞。火幣Pro也同期檢測到TXID為0x0775e55c402281e8ff24cf37d6f2079bf2a768cf7254593287b5f8a0f621fb83的異常。受此影響,火幣Pro暫停所有幣種的充提幣業務。當天,截止暫停交易,SMT在火幣Pro的價格下跌近20%。

而這類漏洞不是第一次發生了,距離上一次發生僅隔了三天。

在4月22日,BEC出現異常交易,據分析,BEC 智慧合約中的 batchTransfer 批量轉賬函式存在漏洞,攻擊者可傳入很大的 value 數值,使 cnt *value 後超過 unit256 的最大值使其溢位導致 amount 變為 0。

簡單的說,BEC的某一段程式碼忘記使用safeMath方法,導致系統產生了整數溢位漏洞,利用該漏洞,黑客可以通過轉賬手段生成大量原本合約中不存在的代幣,並將這些“無中生有”的代幣在市場進行拋售。

由於黑客轉出的代幣數量遠遠超過BEC發行數量70億枚,加之由此引發的恐慌拋售,BEC的64億市值瞬間幾乎歸零。

在這兩起事件之後 PeckShield 團隊利用自動化系統掃描以太坊智慧合約並對它們進行分析。結果發現,多個 ERC-20 智慧合約都存在 BatchOverFlow 安全隱患。若不做好嚴格的程式碼審計和安全防護,億級資金的損失只在一瞬間。那怎麼才能避免這種情況發生呢?以下是已發現的智慧合約攻擊方式,為了資產安全,你必須知曉並在寫智慧合約時避開這些漏洞。


競態條件引發的2種漏洞

競態條件(race condition)就是指裝置或系統出現不恰當的執行時序,而得到不正確的結果。

在執行智慧合約時呼叫外部合約有很大的風險,因為這個外部合約可以接管你當前合約的控制流程,惡意的外部合約可能會更改你合約中的關鍵資料,這對當前合約造成的影響是巨大的。兩個合約繞來繞去,是不是聽起來很拗口?通俗地給你解釋一下。

設想一下當你在轉賬時突然有個人出現在你的面前,打斷了你的操作並趁你不注意修改了你的轉賬資訊,當你發現錢款轉錯人後已為時已晚。這種漏洞有很多種表現形式,它也是史上最大智慧合約漏洞事件——The DAO的“罪魁禍首”。The DAO事件造成了價值6000萬美元的以太坊被盜,且6000萬美元的損失是按當時17.5美元的以太坊價格估算得出的,這也導致了以太坊當時的硬分叉。

漏洞一:函式可重入性

可重入性(Reentrancy)一般可以理解為一個函式在同時多次呼叫,例如作業系統在程序排程過程中,或者微控制器、處理器等的中斷的時候會發生重入的現象。

這個漏洞第一種可能出現的情況是:在呼叫其他函式的操作完成之前,這個被調的函式可能會多次執行。這可能會導致智慧合約中的幾個函式以破壞性的方式進行互動。

640?wx_fmt=png

因為使用者的餘額一直沒有被置0,直到函式執行的結束。第二次(之後一次)呼叫其他函式的操作仍會成功,並且會一次一次地取消對賬戶餘額的置0操作。The DAO事件中以太坊被盜就是因為攻擊者執行了這樣的操作。

解決方案,在給出的示例中,為了避免碰到這個漏洞,我們的解決方案是:使用函式send()而不是函式call.value()(),這將阻止任何外部程式碼的執行。

但是如果無法避免要呼叫外部函式時防止這種攻擊的下一個簡便方法就是確保在你呼叫外部函式時已完成所有要執行的內部操作。

640?wx_fmt=png

請注意,如果你有另一個函式也呼叫了withdrawBalance(),那麼它也可能會受到相同的攻擊,因此你必須將這種呼叫不可信合約的函式視為不可信函式,接下來我會進一步討論潛在的解決方案。

漏洞二:跨函式的競態條件

攻擊者也可以對共享相同狀態的兩個不同函式進行類似的攻擊。

640?wx_fmt=png

在這種情況下,攻擊者可以在程式碼執行到呼叫withdrawBalance()時呼叫transfer() 函式,由於他們的餘額在此時還未被置0,所以即使他們已經收到退款,他們也還能轉移通證,這個漏洞也被用在了The DAO事件中。

同樣的原理,同樣的注意事項。注意在這個例子中,這兩個函式都是同一個智慧合約的組成部分,同樣的,當多個合約共享同一狀態時,這幾個合約之間也可能會出現這個漏洞。

由於競態條件可能發生在多個函式之間,甚至是多個智慧合約之間,所以旨在防止重入現象的解決方案都是明顯不夠的。

解決方案,這兒有兩種解決方案,一是我們建議先完成所有的內部工作,然後再呼叫外部函式;二是使用互斥鎖。

1.首先第一種解決方案,先完成所有的內部工作,然後再呼叫外部函式。如果你在編寫智慧合約時仔細地遵循這個規則,那麼就可以避免出現競態條件。但是,你不僅需要注意避免過早地呼叫外部函式,還要注意這個外部函式呼叫的外部函式,例如,下面的操作就是不安全的。

640?wx_fmt=png

儘管函式getFirstWithdrawalBonus()不直接呼叫外部的合約,但在函式withdraw()中的呼叫足以使其進入競態條件之中。因此,你需要將函式withdraw()視為不可信函式。

640?wx_fmt=png

除了修復漏洞使這種重入現象變得不可能外,還要標記出不可信的函式。這種標記要注意一次次的呼叫關係,因為函式untrustedGetFirstWithdrawalBonus()呼叫了不可信函式untrustedWithdraw(),這意味著呼叫了一個外部的合約,因此你必須將函式untrustedGetFirstWithdrawalBonus()也列為不可信函式。

2.第二中解決方案是使用互斥鎖。即讓你“鎖定”某些狀態,後期只能由鎖的所有者對這些狀態進行更改,如下所示,這是一個簡單的例子:

640?wx_fmt=png

如果使用者在第一次呼叫結束前嘗試再次呼叫withdraw() 函式,那麼這個鎖定會阻止這個操作,從而使執行結果不受影響。這可能是一種有效的解決方案,但是當你要同時執行多個合約時,這種方案也會變得很棘手,以下是一個不安全的例子:

640?wx_fmt=png

這種情況下攻擊者可以呼叫函式getLock()鎖定合約,然後不再呼叫函式releaseLock()解鎖合約。如果他們這樣做,那麼合約將被永久鎖定,並且永遠不能做出進一步的更改。如果你使用互斥鎖來防止競態條件,你需要確保不會出現這種聲明瞭鎖定但永遠沒有解鎖的情況。在編寫智慧合約時使用互斥鎖還有很多其他的潛在風險,例如死鎖或活鎖。如果你決定採用這種方式,一定要大量閱讀關於互斥鎖的文獻,避免“踩雷”。

有些人可能會反對使用競態條件這個術語,因為以太坊並沒有真正地實現並行性。然而,邏輯上不同的程序爭奪資源的基本特徵仍然存在,所以同樣的漏洞和潛在的解決方案也同樣適用。

交易順序依賴與非法預先交易導致的漏洞

交易順序依賴(Transaction-Ordering Dependence,TOD)

非法預先交易(Front Running)非法預先交易是經紀人從客戶交易中獲利的一種不道德做法。在手中持有客戶交易委託的情況下搶先為自己的賬戶進行交易。

以下是區塊鏈固有的不同型別的競態條件:在區塊內部,交易本身的順序很容易受到人為操控。

由於在礦工挖礦時,每筆交易都會在記憶體池中待一段時間,因此可以想象到交易被打包進區塊前會發生什麼。對於去中心化的市場,可更改的交易順序會帶來很多的麻煩。比如市場上常見的買入某些代幣的交易。而防範這一點十分地困難,因為它會涉及到合約中具體的實現細節。例如,在去中心化市場中,由於可以防止高頻交易,故批量拍賣的效果更好。另一種解決方法就是採用預先提交方案的機制,彆著急,後面我會詳細介紹這個機制的細節。

漏洞三:時間戳依賴

請注意,區塊的時間戳可被礦工人為操縱,所以要留意時間戳的所有直接和間接使用。

還有很多與時間戳相關的注意事項,程式設計前一定要認真學習。

整數的上溢和下溢導致的漏洞

想象一個很簡單的轉移通證的場景:

640?wx_fmt=png

如果你的賬戶餘額達到了以太坊中最大的無符號整型值(2^256),那麼你的餘額再增加就無法表示了。因為平時遇到這種現象進位就可以了,但在這裡無符號整型值只有256位,進位的第257位是不顯示的,所以你沒有猜錯,當你進位後你的餘額就會回到0。在電腦科學中這種現象就叫做整數的上溢。

當然了,這種現象也不太常見,因為它需要同時保證你真的有這麼多餘額,你的智慧合約中還沒考慮到上溢問題。考慮一下這個無符號整型值是否有機會達到這麼大一個數字,再考慮一下這個無符號整型值如果改變當前數值,以及誰有權做出這樣的改變。如果智慧合約中任何使用者都可以呼叫函式來更新這個無符號整型值,那麼這個智慧合約就會很容易受到攻擊。如果只有管理員可以做出更改,那麼它才可能是安全的。如果合約中規定使用者的賬戶餘額每次只能增加1,那麼這個合約可能也很安全,因為現在還沒有可行的方法讓你短時間內達到這個限制。

賬戶餘額達到最大時再增加就會被清零,你會瞬間從最富有的人變成最窮的人。不知你有沒有想到可以從最窮的人變成最富有的人?沒錯,下溢也是這個道理,如果這個無符號整型值小於0,那麼它需要向前借位,而借的那一位並不顯示,所以你的餘額就會下溢達到最大值。

看到這裡,你一定要小心使用像8位,16位和24位的無符號整型值,因為8位無符號整型值最大僅可以表示255,所以相比之下它們更容易達到最大值而發生溢位現象。

對待溢位現象請千萬小心,之前有程式設計師整理了20個智慧合約中上溢和下溢的場景。

漏洞四:儲存操作中的深度下溢

Doug Hoyte在2017年的以太坊黑客比賽中提出了這個漏洞,這也讓他獲得了比賽中的榮譽。這個想法很有意思,因為它引起了人們對C類語言下溢如何影響以太坊程式語言Solidity的擔憂。這是一個簡化了的版本:

640?wx_fmt=png

一般來說,如果不經過keccak256雜湊計算(當然,這是不現實的),變數manipulateMe的儲存位置就不會被影響。但由於動態陣列是按順序儲存的,如果攻擊者想要改變manipulateMe這個變數,他只需要這樣做:

  1. 呼叫函式popBonusCode()來實現下溢。(請注意,以太坊程式語言Solidity並沒有內建的pop函式。)

  2. 計算變數manipulateMe的儲存位置。

  3. 使用函式modifyBonusCode()修改和更新變數manipulateMe的值。

實際上,人們都知道這種陣列存在的漏洞。但如果這樣的陣列被掩埋在更復雜的智慧合約架構之下,誰又能輕易發現呢?這樣它就可以任意地對變數進行惡意篡改。

解決方案,在考慮使用動態陣列時,使用一個容器式的資料結構是一種不錯的選擇。Solidity CRUD的第1部分和第2部分文章詳細介紹了這個漏洞。

意外恢復導致的漏洞

漏洞五:利用交易失敗,促使意外恢復

考慮一個簡單的拍賣合同:

640?wx_fmt=png

當智慧合約準備給商品原主人付款時,如果付款失敗,它將恢復。這意味著一個惡意的投標人可以在拍下商品的同時確保給商品原主人的付款總是付款失敗。這樣他們可以阻止其他人呼叫bid()函式,成為商品的新主人。如前所述,為了資金安全,建議拍賣時建立一個預授權方式的付款合約。

另一個例子是當智慧合約通過陣列的迭代向用戶付款時,例如給眾籌合約的支持者退款。通常要確保每筆付款都成功,如果哪一筆付款失敗了,則會恢復,重新付款。問題是如果一筆付款失敗了,那麼你要恢復整個付款系統。這意味著如果哪一筆付款卡住了,這次迭代付款永遠都不會完成,因為一個地址出錯,所有人都拿不到這筆錢。

640?wx_fmt=png

解決方案,這裡我們的建議是使用預授權方式付款。

區塊燃料限制導致的漏洞

漏洞六:利用區塊燃料上限引發漏洞

你可能已經注意到了前一個例子中的另一個問題:如果要一次性地支付給所有人,你可能會遇到達到區塊中燃料上限的情況。每個以太坊的區塊都只能處理一定的最大計算量,如果你試圖超過這個限制,那麼你的交易將會失敗。

即使沒有黑客故意攻擊你,這都是一個問題。如果攻擊者能夠操控你所需的燃料,情況就會變得更加糟糕。在前面的例子中,攻擊者可以新增一堆地址,每個地址都需要很少量的退款。因此,加上給攻擊者地址退款使用的燃料,可能會導致超過區塊燃料上限,從而阻止退款交易的發生。

解決方案,我們推薦使用預授權方式付款來解決這個漏洞。

如果你絕對需要遍歷未知大小的陣列,那麼你應該規劃一下應該把它們分到多少個區塊中,每個區塊需要多少筆交易。這樣你只需要留意現在進行到哪個區塊中的交易了,出錯後僅需從當前區塊開始恢復,如下所示:

640?wx_fmt=png

你需要確保在等待payOut()函式的下一次迭代時處理的其他交易不出現錯誤。所以只有在絕對必要的時候再使用這種模式。

強行給智慧合約中加入以太幣導致的漏洞

漏洞七:強行給智慧合約中加入以太幣,引發程式邏輯漏洞

原則上,我們可以將以太幣強制傳送到智慧合約中而不觸發回退函式。當給回退函式加入重要功能或計算智慧合約的收支平衡時,這是一個重要的考慮因素。請看下面這個例子:

640?wx_fmt=png

這個智慧合約的邏輯似乎不允許對智慧合約付款,以防發生一些“不好的事情”。但是還是存在一些方法可以強制將以太幣送到合約中,使智慧合約的餘額大於0。

智慧合約中的自毀方法允許使用者向指定的受益人傳送任意數量的以太幣,而這個自毀方法並不會觸及合約的回退功能。

在部署一個智慧合約之前,可以預先算出合約的地址並將以太幣傳送到該地址。

解決方案,智慧合約的開發者應該意識到以太幣可以被強制送到智慧合約中,並應該相應地設計智慧合約邏輯。一般情況下,需要假設無法限制智慧合約的資金來源。

對已被棄用的協議進行攻擊導致的漏洞

漏洞八:利用已被棄用的協議進行攻擊

這些攻擊由於以太坊協議的改變或以太坊程式語言solidity的改進而不能使用。在這裡記錄僅供參考,不做過多說明。

熱門文章

640?wx_fmt=gif

營長招人啦

CSDN區塊鏈大本營招採編2名:

1. 對區塊鏈技術有一定的理解,對大公司動態有極強敏感性,且有深度剖析的楞勁兒。

2. 出稿速度快,具有每天報題和出稿的能力。

3. 有能力採訪行業高階人物,能將其觀點進行深刻呈現。

4. 相關媒體經驗2年以上。

5. 英語6級以上,能快速編譯。

6. 本科以上

待遇:

絕不低於行業水平,只要有才,我們願意給你一切資源。

郵件傳送:[email protected]

郵件註明:姓名+區塊鏈+採編應聘

相關推薦

竟然爆發漏洞事件我們如何

“現在進入你還是先行者,最後觀望者進場才是韭菜。”美圖董事長蔡文勝在三點鐘群中的預言一語成讖。在

java 打漁晒網 計算日期相差天數的方法

java 計算兩日期相差天數的方法 題目: 從1990-01-01開始三天打漁兩天晒網?問以後的某一天是打漁還是晒網? 函式 輸入: 某一天日期 輸出: 是打漁還是晒網 返回: 無 dome /* * “三天打漁兩天晒網”

打漁,晒網小演算法

問題描述:    中國有句俗語叫“三天打魚兩天晒網”。某人從2010年1月1日起開始“三天打魚兩天晒網”,問這個人在以後的某一天中是“打魚”還是“晒網”。用C或C++語言實現程式解決問題。 原始碼: //檔名:fishing or relaxing.c

打魚,晒網”(java)

題目描述 某人從2010年1月1日起開始“三天打魚兩天晒網”,問這個人在以後的某一天中是“打魚”還是“晒網”。用C或C++語言/java/python實現程式解決問題。 程式流程圖 原始碼 package zuoye; impor

合約漏洞集錦——分析、模擬與重現

以太坊的智慧合約,自 DAO以來,漏洞就沒斷過,覺得有必要做一下彙總,以為鑑。 1,The DAO 漏洞。 該漏洞直接導致了以太坊的分叉。應該算是最有名的明星漏洞了。 我們將原始碼的無關部分剔除,只剩關鍵程式碼。看模擬原始碼: pragma solidity ^0.4.18;

go-ethereum客戶端(種全節點啟動模式

這篇部落格介紹一下go-ethereum全節點的兩種啟動模式:主網路快速啟動和測試網路快速啟動。這也是客戶端所提供的兩種啟動方式,直接拿來使用即可。下面具體介紹一下使用方法。 主網路快速啟動 其實,我們大多數人再使用節點的時候並不關係之前的歷史資料。我們啟動

2019年區塊鏈最懸念:誰將撿掉落的王冠

以太坊的頭號智慧合約公鏈地位岌岌可危 區塊鏈產業最令人興奮的地方,就在於它把“網際網路+”經濟的基礎設施,從“資訊網際網路”升級成了“價值網際網路”,撬開了一個至少萬億市場規模的全新創業、創富入口。而在區塊鏈產業格局中,群雄逐鹿的戰略中心便是智慧合約公鏈,因為它直接決定了價值網際

幣圈Telegram ICO籌8.5億美元, 擬建第代區塊鏈網絡, 目標

創新 思路 移動應用 時也 至少 好友 輸入 價格 crypt 據國外媒體報道,幣圈核心即時通訊工具Telegram本周(至2月17日當周)稍早遞交給美國證券交易委員會(SEC)的一份文件顯示,該公司已經通過ICO籌集到8.5億美元資金,用於開發TON開源網絡、研發和維護T

虛擬貨幣,區塊鏈,交易所,活動送個,AAT幣400,空投2000萬新年糖果,進電報88AAT

區塊鏈 虛擬貨幣 活動 AAT 糖果 送兩個以太坊。 第一步註冊https://www.aacoin.com/m/#/activeLogin?invitedCode=OoUARUcGEb邀請15人有2個以太坊和約400aat第二步拿糖果88個全球第一個基於區塊鏈的收藏品在線拍賣社區,由真

創建自己的區塊鏈遊戲SLOT——代幣(

rdm con there ppi multipl als div play 數組 一個以太坊合約版本的輪盤遊戲,向合約轉賬ETH,有幾率獲得3,5,10,100倍獎勵 合約地址:0x53DA598E70a1505Ad95cBF17fc5DCA0d2c51174b 捐贈ET

扼殺孩子樂觀性格的10語錄 快看看嗎?

AI 努力 玩具 爸爸 是我 原本 能夠 失望 感恩 陽光般溫暖燦爛的笑容、平和開朗的脾氣、遇事從容不緊張、自信快樂的寶貝,相信每個媽媽都希望擁有吧。但有時我們幾句無心的話,卻會扼殺孩子的樂觀性格,這些話你有沒有說過呢?  當孩子被贊賞時說 |沒有沒有,我們並沒有這麽優

2.0? 親歷3的Devcon我看到了這樣一個 | 見聞錄

  有人說,區塊鏈最大的應用就是發行 Token 和開會。作為從業者,這一年多下來,我也參加過許多會議。但深深覺得以太坊 Devcon 是區塊鏈技術行業中最高水平的會議,這場會議指明瞭以太坊2.0的發展規劃與路徑,這是一場純粹的技術會議,參與者都是有高度技術信仰的程式設計師與

程式設計師:其實我覺得吧,壓力也沒那麼網友:那班嗎?

很多年前人說男怕入錯行,女怕嫁錯郎。一入IT就是豬狗不如的生活,嫁人千萬別嫁IT程式猿,現在過了才十年左右,沒想到程式猿已經大翻身了,不信你看看BAT的工資,高的嚇人,夠我搬好久磚了。 如果有正在學java的程式設計師,可來我們的java技術學習扣qun哦:72340,3928,小編花了近一個月

什麼是DAO?(

Decentralized Autonomous Organization,簡稱DAO,以太坊中重要的概念。一般翻譯為去中心化的自治組織。 投票支付合約的所有費用和行動需要時間,並要求使用者始終保持活躍,知情和專注。另一個有趣的方法是選擇一個可以控制合約的指定賬戶,然後能夠迅速做出決定。 流動民主合約 我們將

死磕 Elasticsearch 方法論:普通程式設計師高效精進的 10 狠招(Elasticsearch程式章)|MVP講堂

作者:阿里雲MVP 銘毅 下節連結: Elasticsearch學習,請先看這一篇! 開篇 人工智慧、大資料快速發展的今天,對於 TB 甚至 PB 級大資料的快速檢索已然成為剛需。Elasticsearch 作為開源領域的後起之秀,從2010年至今得到飛躍式的發展。 Elasticsearch 以其開

智慧合約的種資料分離模式(部署可升級式智慧合約)

重要! 做資料分離推薦使用2018年後的的Geth版本,即v1.8以上。在genesis.json創世檔案的配置config裡需新增拜占庭Block,如下: "config": {     "chainId": 1,     "homesteadBlock": 0,   

網際網路公司各崗位真實工作內容底,沒錯說的就是

產品部 產品經理 網際網路公司最名不副實的“經理”沒有之一,需要在跟技術人員溝通之前就表明立場:PHP到底是不是最好用的語言?這將直接決定後期跟技術人員的合作效率。 日常精神分裂,跟老闆談情懷,跟技術談運營,跟運營談產品,跟產品談技術。 口頭禪:“我改!”“我改還不行嗎?!”

《我學區塊鏈》—— 十三、開發者資源工具集

三十三、以太坊安全之 以太坊開發者資源工具 開發語言、框架與工具 語言 Solidity - 官方推薦以太坊智慧合約開發語言,也是目前最為主流的智慧合約語言。 Bamboo - 是一種將智慧合約描述為有限狀態機的語言,把智慧合約看成一個狀態和交易的函式,同時

《我學區塊鏈》—— 十四、智慧合約靜態安全分析

三十四、以太坊智慧合約靜態安全分析        以太坊的智慧合約程式碼審計,筆者找到兩種方式:一是 CertiK,一個提供智慧合約安全服務的區塊鏈平臺,是一條公鏈系統,採用 PoP(proof-of-proof)共識機制,用來分析合約需要消耗該區塊鏈平臺的原生

《我學區塊鏈》—— 十六、批量轉賬(空投)節省費用

三十五、智慧合約收發 ETH 詳解        前段時間 fcoin 的空投把 eth 堵得不成樣,看見好幾個空投竟然是一個個地 transfer轉賬,但是實際上如果用合約實現批量轉賬,不管是成功率還是效率都會高很多,還省 gas。        整個過程的模