作者:yudan@慢霧安全團隊 & Jerry@EOS Live 錢包
公眾號:慢霧科技
本篇攻擊手法技術分析基于 EOS 1.8 以前版本,在新版本中未做測試。
0x01 事件回顧
根據慢霧區情報,EOS DApp EOSPlay 中的 DICE 游戲于9月14日晚遭受新型隨機數攻擊,損失金額高達數萬 EOS。經過慢霧安全團隊的分析,發現攻擊者(賬號:muma**mm)在此次攻擊過程中利用 EOS 中的經濟模型的缺陷,使用了一種新型的隨機數攻擊手法對項目方進行攻擊。
在開始詳細的細節剖析之前,需要先了解一些技術背景作為鋪墊。
0x02 技術背景
我們知道,在 EOS 系統中,資源是保證整個 EOS 系統安全運行的關鍵,在 EOS 上發出的每一筆交易都要消耗對應的資源,如 CPU、NET 或 RAM,而這些資源都是需要一定量的 EOS 作為抵押換取的,如果抵押的 EOS 的數量不夠,則無法換取足夠的資源發起交易。而本次的攻擊行為則是利用了 EOS 資源里面的 CPU 資源模型進行攻擊。我們知道,在 EOS 上 CPU 是一個十分重要,也是消耗最多的資源。在 EOS DApp 活躍的時候,會經常出現 CPU 不足的情況,為此,還催生出許多做 CPU 抵押的第三方 DApp,緩解資源使用緊張的問題。那么 CPU 資源的份額具體是怎么計算的呢?
參考 MEETONE 的文章我們可以看到 CPU 的計算方式為
max_block_cpu_usage * (account_cpu_usage_average_window_ms / block_interval_ms) * your_staked_cpu_count / total_cpu_count
其中,max_block_cpu_usage * (account_cpu_usage_average_window_ms / block_interval_ms) 是一個固定的變量,也就是常量。那么真正影響帳號的 CPU 使用情況的是后面的公式 your_staked_cpu_count / total_cpu_count,這里的意思就是,你抵押的 CPU 總量除以總的 CPU 抵押總量,那么,如果總量變多了,如果你的抵押數量沒跟上去,能用的 CPU 就會變得越來越少。而 CPU 不足,則無法正常地發起交易。在了解完 CPU 的相關知識后,就可以開始相關的分析了。
0x03 攻擊細節剖析
本次的攻擊帳號為 muma**mm,通過分析該帳號行為,發現該帳號通過 start 操作發送了大量的 defer 交易,這些 defer 操作里面又發起了另一個 start 操作,然后無限循環下去。

由于這些大量的交易充斥著區塊,為分析工作帶來了困難,所以首先要對這些交易進行篩選。查看攻擊者帳號的接收開獎通知的交易,通過查詢游戲的開獎區塊,我們發現一個問題,就是在多個開獎區塊內只有系統的 onblock 一筆交易,說明這個區塊為空塊。很明顯,這是因為 CPU 價格的太高導致交易無法發出而出現的情況。而本次受攻擊的 EOS Play 采用的是使用未來區塊哈希進行開獎。

那么攻擊者為什么要選擇這樣做呢?我們來分析下區塊哈希的計算方法

通過上圖我們可以看到,區塊哈希的計算最終調用了 digest() 方法,而 digest() 方法的實現為對 block_header 結構體本身進行哈希。我們繼續看 block_header 的定義

可以看到 block_header 結構體的變量分別有
- confrimed
- previous
- transaction_mroot
- action_mroot
- schedule_version
- new_producers
- header_extensions
其代表的含義分別為
- 每一輪開始前需要確認的上一輪生產的區塊數
- 之前區塊的區塊哈希
- 區塊 transaction 的默克爾根哈希
- 區塊 action 的默克爾根哈希
- 節點出塊順序表版本,這個在一段時間內是不會改變的
- 新的出塊節點,可為空
- 拓展,1.8 之前這個變量為空
到了這里,我們可以清楚的看到,除了 action_mroot 和 transaction_mroot 外,所有的其他變量都是可以輕松獲取的,然后我們繼續深入看下 transaction_mroot 和 action_mroot 的計算方法。

可以看到 transaction_mroot 和 action_mroot 是分別對 pending_block 內的 action 和 transaction 的總的 digests 進行一個 merkle 運算最終得到一個 transaction_mroot 和 action_mroot。我們繼續追蹤各自的 digest 的生成方式

以上兩條分別是 transaction digest 和 action digest 的生成方式,我們可以看到,transsaction digest 的計算使用到了 cpu 和 net 作為因子,這兩個因子取決于交易執行時 bp 的節點狀態,雖然透明但較難準確預測,而 action digest 沒有使用到這兩個變量,可以輕松的算出來。那么分析到這里,我們可以確定的是,要預測一個區塊的哈希,需要引入不確定的 cpu 和 net 的使用量,那么,有沒有辦法不將這兩個因子引入到計算當中呢?
答案是必然的!
我們把目光聚集到項目方開獎區塊的唯一一個交易 onblock 中,首先我們先看看 onblock 的生成定義

以上是構造 onblock 操作的一些定義,可以看到,onblock 操作的 data 字段為 header_block_header,就是當前的區塊頭信息,即相對于當前 pending 區塊上一個塊的區塊信息,接下來我們追蹤該函數的調用過程。

發現一個點,就是這里會把 onblock 交易的 implict 設置為 true,這樣設置有什么用效果呢? 我們接下來看下交易的打包過程

這里看到,由于在區塊開始的時候,implict 屬性被設置為 true,導致交易在被打包時 onblock 交易不會進入 pending 區塊的 transaction 序列中,但是卻進入了 pending 區塊的 action 序列。也就是說,項目方的開獎區塊的 action 序列不為空,而 transaction 序列為空。所以說開獎區塊的 transaction_mroot 必然為 0,這樣,就避開了計算 transaction_mroot 時引入的 cpu 和 net 這些不確定因素,只計算 action_mroot 即可。而 action_mroot 的計算只需要對 onblock action 本身進行計算即可,且 onblock 的 data 是可以拿到的,即當前區塊頭信息,也就是相對于當前 pending 區塊的上一個區塊的信息。那么到了這里,攻擊者已經完全可以拿到可計算開獎區塊哈希 的所有信息,計算出開獎區塊哈希 就不是什么難事了。
0x04 攻擊情況總結
在完成攻擊細節剖析后,我們可以知道,攻擊者在下注時,并不知道開獎區塊的區塊哈希,因為該哈希和開獎區塊的前一個塊相關,所以攻擊者能做的是在開獎區塊的前一個塊對開獎區塊的信息進行計算。那萬一計算不對怎么辦呢?關于這點,我們發現一個現象,當區塊計算結果和預期有誤差時,攻擊者會在開獎區塊發送一筆 hi 交易來企圖改變結果,但是由于新引入的 hi 交易會引入 cpu 和 net 使用量這些變量,所以引入 hi 交易并不能確保結果可控,但是已經將贏的概率大大提升了,相當于重新開獎。

總結下來攻擊者的攻擊手法為:
- 通過 REX 購買大量的 CPU,導致正常交易無法發出
- 在假設開獎區塊為空塊的情況下對開獎區塊的哈希進行計算
- 當發現計算結果不對時,向開獎區塊內發送一筆 hi 交易,嘗試改變結果。
0x05 預防方案
EOS Play 由于使用了未來區塊哈希作為開獎因子,導致開獎結果被預測從而導致攻擊發生。這次的例子再一次的告訴我們,所有使用區塊信息進行開獎的開獎方案都是不成熟的,都存在被預測的可能。特別是 REX 出現以后,抬高 CPU 價格比以往變得更為容易。
慢霧安全團隊在此提醒,DApp 項目方在使用隨機數時,特別是涉及到關鍵操作的隨機數,應盡量采用更為安全的線下隨機數生成方案,降低隨機數被攻擊的風險。除此之外,同時還可以接入慢霧安全團隊孵化的 EOS 智能合約防火墻 FireWall.X(https://firewallx.io),為合約安全保駕護航。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/1042/
暫無評論