【精彩好文推薦】EOS主網上線以來智慧合約攻擊方式彙總

買賣虛擬貨幣


EOS主網上線以來智慧合約攻擊方式彙總

為什麼分享這個主題

自六月份EOS主網之後,親身經歷和見證過EOS DApp一路走來的艱辛。

被駭客以各種各樣方式攻擊過的DAPP,總的損失規模至少上千萬了。

一直以來,都想對所有被攻擊過的DAPP,以及攻擊方式做一個彙總。

因為前人花大的代價踩的坑,得到的教訓,應該總結好,避免之後的人再犯。

引用下 imeos 總結的 安全漏洞盤點:

分享內容

這些造成損失的漏洞,可以歸為合約程式碼漏洞,和隨機數演算法使用不當的兩種問題。

1. 智慧合約程式碼問題

程式碼溢位之類的漏洞 (狼人殺案例)

這個就不多說了,漏洞細節,就是當時eos版本中的 asset 類的乘法存在 檢查溢位無效的問題。 而程式碼中,依賴 asset 的程式碼做溢位檢查,或者壓根就沒考慮做溢位檢查。 導致了整型溢位,出現了致命而無法修復的bug。

這點,只要主要在合約程式碼中的數值計算部分,做詳細的 eosio_assert 斷言檢查就可避免。關鍵還是細心寫程式碼哈。

未檢查 code == eosio.token

這個問題受害面還是比較廣的,從 EOS bet、newdex、 以及後來的 cast 專案,都是因為沒有檢查轉賬的來源,而蒙受大額損失,從以上的列表看出,這條經驗是好百萬鉅款買來的。

專案方,利用轉賬通知回撥transfer,來執行業務,但是這個 transfer通知,有可能來自 非 eosio.token的 (俗稱假幣 假EOS), 所以對 code == eosio.token 的判斷,就非常的重要。

轉賬通知 未判斷 to == _self

這個問題,暴漏在 EOS bet 第二次被攻擊的時候。損失也是500萬以上。

轉賬通知,有個特點,就是不僅僅你收到轉賬的會收到通知,任何人收到轉賬的時候,都可以通知你,他收到了轉賬。

比如,張三收到了李四 100 EOS 的轉賬,於是張三 告訴王五,“收到 100 EOS 轉賬”。

王五,沒有判斷 “收款人 等於王五”,於是以為是自己收到了 100 EOS的轉賬。

原因就是這樣。 專案方,實際上沒有收到 真正的 EOS, 只是收到了 一條EOS 到賬通知,但收款人還不是專案方。

如何對任意人發起轉賬通知呢?

只需要在轉賬中加入一條程式碼即可:

require_recipient(N(eosbetdice11));

這樣,你想通知誰,就通知誰。 此時要是對面沒判斷收款人,那麼很可能就被你黑了噢。

2. 偽隨機程式碼中存在的一些問題

偽隨機數演算法中的可計算引數

之前不少概率隨機遊戲的專案方,採用了鏈上偽隨機方案,也即純粹使用區塊鏈上的資料來做遊戲開獎的隨機數種子。

但是,由於使用不當,有了種種被黑的結局。

類似的鏈上偽隨機演算法,我貼幾張圖作為參考:

以及廣泛被引用的github程式碼:

https://github.com/generEOS/eosio.random/blob/master/random.cpp

以上案例,都有一個共同的特點,就是採用了 tapos_block_prefix, tapos_block_num 做為隨機數種子。

但是啊,這兩個值,其實是依賴於 過去的區塊。也就是說,它們是能算出來的,當這個關鍵的引數,能夠被計算出來的時候,所謂的隨機數演算法,就不是隨機了。

駭客可以提前計算好結果,保證百分百勝率。

除了,tapos_block_prefix, tapos_block_num, 還有 當前transacation_id 下面就分享下,這幾個值如何算。

tapos_block_prefix, tapos_block_num 以及 transacation id 怎麼計算 ?

tapos 有個定義是 Transactions as Proof-of-Stake (TaPOS)

它是指定一個過去的區塊( ref_block_num ),用來做 Proof-of-Stake的。

而程式碼中使用的 tapos_block_prefix 和tapos_block_num, 正是由這個 ref_block_num 算出來的。

檢視push action 說明,可以看到發起一個普通的action時,其中 ref_block_num, 是由客戶端指定的!

有人會說,我平時發起action的時候,並沒有指定過 ref_block_num啊?

其實,當你沒指定 ref_block_num 的時候,我們使用的 cleos 或eosjs 客戶端,會幫我們預設指定一個:

翻客戶端的程式碼,它告訴你,如果使用者沒指定ref_block_num,會幫你取一個, 取的是 last_irreversible_block_id,

也就是get info 中返回的,上一個不可逆區塊的id。

也就是,有了ref_block_num ,我們就可以拿到 tapos_block_prefix 和tapos_block_num 了:

tapos_block_num = ref_block_num & 0xffff
tapos_block_prefix = get block(ref_block_num).ref_block_prefix

最後 說下 transacation_id ,

transacation_id , 是一筆交易的唯一id,是一個hash值,看起來好像是隨機不可預測的啊。

於是有人拿它做隨機數,然後就沒然後了。

這個值,跟你發起的 transacation 有唯一關聯,
所以是個可計算的值。

計算方法如下:

直接 read_transaction 拿到 當前 transacation的資料,然後 sha256 就得到了 transacation_id了。

3 為什麼利用延時交易,tapos_block_prefix, tapos_block_num 依然被破解了

有的專案,採用延時交易來非同步開獎,偽隨機演算法裡,用到了 tapos_block_prefix, tapos_block_num, 時間,使用者名稱,獎池金額等資訊。

這樣的偽隨機演算法,看起來好像沒毛病,用到了未來的資料,似乎無法預測,但是真的是這樣麼?

時間的話,知道了你延遲多少秒後,直接往上加就行了,使用者名稱是確定的,獎池金額這個雖然是動態的,但是如果變動的頻率不是非常快,快到每分每妙都在變,那麼在短時間內,可以視為是定值。

所以關鍵還在於 tapos_block_prefix, tapos_block_num 。

延遲交易中的 tapos_block_prefix, tapos_block_num, 和普通交易中的 tapos_block_prefix, tapos_block_num 計算方式有所不同。

普通交易,透過客戶端制定 ref_block_num ,然後計算出來。

而延遲交易中,看EOS相關程式碼得知,直接使用head_block_id 做為 ref_block_num 。

也就是說,當前區塊頭,就是ref_block_nu。

知道這點後,就好辦了,有多種辦法可以拿到head_block ,比如說 直接 get_info

拿到了 head_block_num 之後,在get_block 資訊,可以直接得到

拿到了這兩個引數,又知道了隨機數演算法,理論上,就能夠預測能不能中獎了。

直接在合約裡寫程式碼判斷,能中獎就傳送交易,不能就跳過。

總結 tapos_block_prefix, tapos_block_num,慎用為隨機數演算法引數,如果非要用,可以採用發起連續的兩次延時交易。因為延遲交易中的 head_block_id 跟你延遲多少秒沒關係,是構造這個延遲交易的時候,就設定好了的。

PS 千萬別在你的偽隨機演算法裡,加入一些可控引數,比如EOS餘額。EOS Dice就是因為這個被攻破了兩次。

4. 同步開獎時被利用智慧合約鉤子,回滾交易

早期被黑的專案中,還存在一些 “錯誤的實踐” , 也值得提一下。

第一種,是同步開獎,同步開獎有個問題,就是駭客可以輕鬆的破解你,一個簡單的思路是,直接拉取你的專案code,然後部署一個合約A, 然後用合約B跟這個A合約玩,假如和A合約玩贏了,A合約會給B轉賬,在B的轉賬通知那邊寫一個轉賬通知回撥,一旦贏了,就和你的專案玩。 這樣是必勝的。

第二種,是發回執,被對方拒絕,導致回滾整個交易。

一個錯誤案例是: 開獎 -> 給對方發一個 receipt action, 告訴對方你中獎沒中獎, 於是對方,直接針對這個receipt 寫一個回撥,當你告訴他輸了之後,他可以拒絕,回滾這次開獎, 這個好像被叫做 “重放攻擊” , 避免的方法,就是 receipt 之類的通知,可以採用非同步的。

5. 為何不開源也被擼?

目前大多數菠菜類專案方,是不敢開源的,一是怕競爭,而是怕暴漏問題。

這個不好評判,我個人還是喜歡開源的。

被擼的專案方,其實大部分也都沒有開源,那麼為什麼還是被擼了呢? 這裡簡單分享下。

首先,你合約程式碼都部署都上鍊了,對於有能力擼你的那些技術大神,這裡其實沒多少秘密可言。無非是讀原始碼和編譯後程式碼的區別而已。

專案的程式碼,在編譯過後,部署到了鏈上。取回的步驟如下。

  1. 第一步,透過 get code 取回。可以得到 abi 檔案和 wast 檔案。abi檔案,可以看到介面資訊,和猜測整個合約程式碼的大致結構。

  2. 翻譯 wast 檔案了,找出你的隨機數演算法。 下面幾張圖,是簡單的示例。

簡而言之,你開源或不開源,對那些駭客來說,其實都一樣的。

關鍵還是寫程式碼時,不要留下問題。不要去踩那些坑。不要輕易使用可以被拿到的引數做關鍵部分的隨機數演算法引數。

6. onchain呼叫

EOS Bet 在第一次被攻擊的時候,表面上,是沒有檢查

code == eosio.token , 實際上,還有一個值得一提的細節。

那就是,對方這個 transfer action,並沒有暴漏在 abi中,也就是客戶端是沒辦法直接呼叫的。

但是駭客是直接呼叫的。那麼是如果做到的呢。

原來,abi 並不是必須的。

一旦你的code中有這個action,那麼就算在abi檔案中,把這個 action介面刪除了,企圖留下外面的人不知道的 “後門”。

實際上這個 action 還是可以被呼叫。

有兩種方法,

第一種,就是直接在另一個智慧合約裡發起呼叫。

第二種,就是改造下你的客戶端。不要在push aciton的時候,去呼叫abi。

改造的細節過於技術,有興趣的去參考這個 https://zhuanlan.zhihu.com/p/42903901?utm_source=wechat_session&utm_medium=social

總結, abi 只是介面和資料描述檔案,就算你程式碼不部署 abi,也不影響合約程式碼正常工作。 所以,如果你的智慧合約,僅僅是用來和其他智慧合約互動,而不是面向客戶的使用者(cleos、eosjs),那 abi 都可以不用部署。

作者主頁 https://bihu.com/people/305100

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;