智慧合約的弱點與攻擊
本文是介紹DASP(Decentralized Application Security Project)最常見的智慧合約漏洞以及這些漏洞是如何被利用的。DASP是收集了以太坊區塊鏈最常見的智慧合約漏洞.
重複輸入(Reentrancy)
智慧合約在執行狀態變更(change)後更新(update)狀態,則可能容易受到重複輸入的攻擊。 如果一個智慧合約的withdraw函數向另一個智慧合約送出value,則呼叫接收者智慧合約的payable fallback函數,接收者的這個函數可以重新呼叫發送者智慧合約的withdraw函數,在使用者帳號餘額的原智慧合約中更新(update)之前進行第二次withdraw。
上面的範例的代碼寫法容易受到再次輸入的攻擊。 在第 3 行中,可能會呼叫智慧合約的 fallback 函數,fallback函數可以再次呼叫msg.sender.call.value函數。 由於智慧合約尚未更新其帳號餘額(在第 4 行執行),攻擊者最終可以拿到原始金額的兩倍。以太坊 DAO 智慧合約被利用到重複輸入的漏洞。以太坊 DAO智慧合約包含與上述範例類似的代碼,讓惡意智慧合約從 DAO 智慧合約中拿到更多的貨幣。
Access Control
智慧合約的所有者與其他使用者通常都會有不同等級的權限。 如果智慧合約的access control程式碼執行不當,則攻擊者可以控制該智慧合約。
上面的程式碼範例主要是將這個智慧合約的所有權權限授予智慧合約的建立者,作為合約初始化過程的一部分。 但是,程式碼沒有檢查 initContract 函數是否已被呼叫,從而讓任何使用者再次呼叫它並宣告合約的所有權。
Parity 錢包是一種常用的基於智慧合約的加密貨幣錢包。 它包括一個 initWallet 函數,這個函數存儲有被授權的使用者清單進行這個錢包交易。 但是,它不包括檢查之前是否呼叫了 initWallet,這樣就讓攻擊者呼叫多個有價值錢包的函數並得到存儲在其中的加密貨幣。
數學計算
常見的計算漏洞包括integer overflow和integer underflow。 當一個variable存儲在一個 fixed-size variable中而沒有驗證這要存儲的variable是否小於fixed-size variable可以包含的最大值時,就會發生integer overflow漏洞。 如果不是integer overflow,而是比較小的value,則這個比較小的value會有“環繞”現象,這是會讓很大的數字看起來很小。integer underflow就是這種狀況,它會從一個小的數字中減去一個大的數字並將結果存儲在一個沒有正負符號variable中,相減完之後看起來是一個非常大的數字。
上面的範例代碼在第 2 行包含一個integer underflow漏洞。如果 _amount 大於發送者的餘額,則會產生一個看起來是一個很大的正數(因為沒有正負符號)並通過檢查。
BatchOverflow 是 PeckSecurity 為他們在網際網路上發現的智慧合約漏洞起的名字。 易受攻擊的合約被設計為向多個接收者發送相同數量的加密貨幣。 狀況是他們會立刻檢查這單次要發送的總額是否全部有效(會有integer overflow),但會分別向每個接收者發送這個加總的貨幣(但不會發生overflow)。
未檢查的返回值
在以太坊中,一些低階函數在失敗時會返回 false 而不是返回一個error。 如果智慧合約沒有檢查低階函數的返回值,他們可能沒有意識到函數失敗了。 在以太坊中發送fail是沒有告警的,但transfer則會發出告警。
上面的智能慧合約範例中,函數在第 5 行呼叫 send 並且不檢查返回值。 Send 是一個低階函數,如果失敗將返回 false。 而此代碼中如果send 收到fail,則智慧合約的帳號餘額將會不正確。
King of the Ether是一個智慧合約,這是一個使用者會支付越來越多的加密貨幣的收益給這個合約的前任owner。 它包括一個未經檢查且只分配了少量的gas的send呼叫。 如果前任owner使用基於智慧合約的錢包,send將會收到fail並沒收使用者的付款。
DoS(Denial of Service)
DoS使智慧約可能無法運行。 兩個常見的DoS漏洞是由很差的access control和無限的loops或遞歸的可能性造成的。
上面的範例代碼顯示了容易受到DoS攻擊的智慧合約代碼。 如果 _largestWinner 設置得夠高,智慧合約將需要大量的 gas 來執行 selectNextWinners 函數。 以太坊有一個內建的最大gas限制,這意味著如果 largestWinner變得太大,該功能可能無法運行。
有問題的隨機性
一些智慧合約需要access一個隨機數。 而獲取“隨機”數字的可以使用:
- secret value
- embedded secret code
- 基於挖掘的統計數據
- 使用一個external oracle
由於智慧合約代碼在區塊鏈上是公開的,因此前兩個選項是不能用的。 惡意礦工可以操縱第三個選項以得到利益。 唯一可行的解決方案是使用區塊鏈的external oracle。
上面的範例使用隨機數作為基於智慧合約的博弈遊戲的一部分。 作為隨機性的來源,它使用區塊鏈上的前一個區塊的hash value。但 無論使用哪個區塊,這個解決方案都不起作用,原因如下:
- 以太坊只存儲最後 256 個區塊hash value,在那之前的資訊都是
- 對於256 個區塊之前的區塊,攻擊者可以建立一個智慧合約來檢查當前區塊是否為winner,如果是,則呼叫此函數來領取收益
SmartBillions 彩券是一款基於以太坊的博弈遊戲,使用者可以預測 6 個“幸運號碼”,如果他們決定呼叫 play 函數時區塊的hash value等於該數字,他們就可以獲得一定的賭注回報 (重複6次以增加獎勵)。 一名玩家理解到以太坊只會存儲最後 256 個區塊hash value,並選擇零當作新運號碼,等待256區塊之後(大約是73分鐘),之後得獎數字就是零了,然後再呼叫won這個函數,而如此重複這樣的作法就保證會一直贏下去。
Race Conditions(競爭條件)
這是兩個不同的程序比賽第一個完成某項任務。 一些區塊鏈容易受到競爭條件的影響,因為交易在公有池中是可見的,並且它們被放置在區塊中的順序取決於交易手續費。 如果智慧合約獎勵用戶完成一個謎題(puzzle),攻擊者可以觀察交易池中的答案,然後提交相同的答案並以更高的交易手續費贏得回報。
上面的範例是King of the Ether代碼的一部分,如果他們支付最低金額就可以將控制權轉移給新的owner。 它很容易受到競爭條件的影響,因為使用者可以觀察另一個使用者的交易來領取回報,並通過提交更高交易手續費來擊敗其他人。
Bancor代幣 是基於智慧合約的加密貨幣交易,主要在讓使用者用以太幣買賣代幣。 Bancor代幣的匯率是基於其當前Bancor代幣的供應量。 攻擊者可以通過利用競爭條件來確保他們在另一個使用者進行類似交易之前搶先執行交易,就可以在 Bancor 上獲得保證的利潤。 贏得一場競賽讓攻擊者低買高賣(因為由於供應量減少,他們的代幣在 Bancor 中的價值增加了,而供應量增加會使代幣價格降低)。
時間戳記依賴性
一些智慧合約只會在特定時間之後執行運作。 由於合約對時間訊息的唯一訊息來源是區塊的時間戳計(這不是固定死的),惡意礦工可以建立一個區塊,允許他們在正確的時間贏得回報(只要時間在可接受的區塊時間戳記內) 。
上面的範例代碼容易受到利用時間戳記依賴性的攻擊。 如果攻擊者設法贏得在可接受所需時間戳記的區間內建立一個區塊的權利,他們可以加入一個交易,並且他們可以領取自己區塊的回報。
GovernMental 智慧合約主要讓使用在 12 小時內下注是否沒有人下新賭注。 如果是這樣,他們將贏得智慧合約中的回報。 由於大多數區塊鏈都有一個兩小時的時間戳記可接受區間,因此在 10 小時內建立區塊的礦工可以贏得回報。
短地址
短地址攻擊利用函數的參數連續性的存放在電腦的memory,並且以太坊會自動將參數填充到正確的長度。 如果函數的參數太短,以太坊會自動從右邊填充參數來修復正它。 如果攻擊者使用一個byte是很短的地址呼叫帶有參數(address ,amount)的函數,這函數將把數量的第一個byte認知為address的最後一個byte,並將右邊的amount認為是一個byte。
上面顯示的 sendCoin 函數容易受到短地址攻擊。 sendCoin 不會對其變量施加大小,因此短地址和amount會被適當地讀取,並且amount通過第 3 行的檢查。但是,在第 1 行中呼叫的 Transfer 函數對金額乘上了 256 。 如果短一個byte,並且amount的第一個byte是攻擊者要找的地址的最後一個byte,則攻擊者將收到 sendCoin 函數認可的 256 倍的以太幣數量。