2018 年 12 月 19 日,衆多遊戲類 DApp 遭遇交易回滾攻擊,其中包括 BetDice,EOSMax,ToBet 等。按當時 18 元人民幣的價格計算,損失超過 500 萬人民幣。期間 BetDice 通過鏈金術平臺發出多次公告 , 一度造成恐慌。

與此同時,慢霧安全團隊對交易所和中心化錢包給出了暫時性的方案。此刻,攻擊手法依舊是一個謎團。那麼,攻擊手段究竟是怎樣的呢?在進行攻擊回顧之前,需要先了解一點技術背景。

技術背景

1、我們知道 EOS 採用的共識算法是 DPOS 算法,採用的是 21 個超級節點輪流出塊的方式。除了 21 個超級節點外的其他全節點,並沒有出塊的權限。起到的作用是將收到的交易廣播出去,然後超級節點將其進行打包。

說到這裏,很容易看出,如果一筆交易是發給除了超級節點外的其他全節點,這筆交易會經歷兩個過程。首先,這筆交易先被全節點接收,然後交易再被節點廣播出去進行打包。而一筆交易是需要超級節點中超過 2/3+1 的節點進行確認之後纔是不可回滾的,也就是不可逆的。

這個過程大概需要 3 分鐘左右,也就是說,交易發到除了超級節點外的全節點的時候,由於全節點沒有打包的權利,此時此刻交易仍然處於可逆狀態(這裏假定節點數據庫的讀取模式爲默認的 speculative,有關閱讀模式的參考:https://developers.eos.io/eosio-nodeos/docs/read-modes)。這是一個核心關鍵點。

2、每一個 bp (超級節點),都可以在自己的節點的 config.ini 文件內進行黑名單的配置,在黑名單中的帳號是不能進行交易的,也就是說無論怎樣,黑名單的交易都會被回滾。

黑名單配置路徑:

Mac OS:

~/Library/Application Support/eosio/nodeos/config/config.ini

Linux:

~/.local/share/eosio/nodeos/config/config.ini

配置方法: 將 config.ini 文件內的 actor-blacklist 填入黑名單帳號,如下圖中,將 attacker 這個帳號作爲黑名單帳號。

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

瞭解了以上的知識點之後,我們就可以進行整個攻擊事件的回顧了。

攻擊回顧

跟蹤攻擊者的其中一個攻擊帳號,發現帳號合約內只有一個 transfer 函數

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

同時,我們可以通過覆盤這個帳號的所有交易記錄發現,這個帳號只有開獎記錄,而沒有下注記錄,看起來就好像項目方故意給這個帳號進行開獎一樣。然而事實上並非如此。那爲什麼會出現這樣的情況呢?這就需要上面的技術背景的知識了。以下是詳細的攻擊手法:

1、首先:攻擊者調用非黑名單合約的 transfer 函數,函數內部有一個 inline action 進行下注,from 填寫的是攻擊者控制的非黑名單合約帳號,to 填寫的是遊戲合約帳號。這時,攻擊者發送交易是發向遊戲合約自己的全節點服務器。使用的是黑名單帳號進行。

2、遊戲節點讀取到了這筆交易,立刻進行開獎,如果中獎,將對攻擊者控制的非黑名單帳號發送 EOS。

3、在經歷了一個 1,2 兩個操作之後。理論上攻擊者控制的非黑名單帳號是進行了餘額扣除。然後進行正常的開獎邏輯。到這裏之前,一切都是正常的。也許有讀者會問,爲什麼配置了黑名單,交易還能正常發起?原因是這個黑名單生效範圍是在 bp 內,普通的全節點的 config.ini 內是沒有黑名單的配置的。所以攻擊者依然可以發起交易。

4、到此爲止,攻擊正式開始,也到了最關鍵的地方,由於項目方節點在收到下注交易的時候已經立馬完成了開獎邏輯,而且採用的是線下開獎的模式,即下注交易和開獎交易是兩筆不同的交易。但是,這兩筆交易僅僅是在項目方的節點內完成,仍然是可逆的。當項目方節點向 bp 廣播這兩筆交易的時候,由於第一筆下注交易的發起者在 bp 節點的黑名單內,這一筆交易將被回滾,也就是打包失敗,而開獎交易的發起者是項目方,不在黑名單之內,會被正常打包。因此兩筆交易中的第一筆下注交易一定會被回滾,而開獎交易依舊會被打包,這也就解釋了爲什麼只有開獎記錄,而沒有下注記錄。因爲下注記錄都被回滾了。

整個過程可以參考下面的圖:

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

攻擊復現

本次攻擊復現參考 EOS LIVE 錢包團隊的文章:https://eos.live/detail/19255

1、環境準備

(1)本地準備兩個節點,一個出塊節點,一個同步節點,出塊節點用於模擬真實 bp,而同步節點則用於模擬項目方,其中出塊節點需要開啓 history 插件,方便後續的 debug,並且把 attacker 加入節點黑名單。方便後續的 debug。打包節點則需要開啓自動開獎插件 , 自動開獎插件配置詳見:

https://github.com/superoneio/security

本次復現用到的代碼:

https://github.com/superoneio/security

本地多節點配置方法官方參考:

https://developers.eos.io/eosio-nodeos/docs/local-multi-node-testnet

(2)三個測試帳號,分別是 tobetioadmin,tobetiologs1,attackproxy1,分別爲項目方帳號,項目方 log 帳號,和攻擊代理帳號,其中 tobetioadmin 部署 tobet 遊戲合約,tobetiologs1 部署 logs 合約,attackproxy1 部署 attack 合約。注意除了攻擊代理帳號外的其他兩個帳號不要改爲其他帳號,如果改爲其他帳號需要對自動開獎插件進行修改,自動開獎插件是攔截 tobetioadmin 這個帳號的。

(3)附上我的雙節點的配置:

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

其中 nodeos_main 爲出塊節點,nodeos_second 爲同步節點。

2、啓動節點

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

看到以上信息則代表 dice_plugin 配置成功

3、首先對正常的邏輯進行測試。

使用 attackproxy1 對 tobetioadmin 帳號進行正常的轉賬交易

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

可以看到,攻擊代理合約進行了正常的轉賬。

4、開始攻擊,使用黑名單帳號調用攻擊代理合約,向項目方合約發起攻擊。
(1)查詢初始餘額

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

(2)爲保證攻擊成功,連續向項目方發起 4 起攻擊

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

(3)再次查詢餘額

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

(4)查詢 attacker 帳號記錄

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

可見,並沒有 attacker 對 attackproxy1 的調用記錄,最後兩條記錄是我測試直接使用黑名單向 tobetadmin 發起攻擊的時候留下的記錄。與本次測試無關。但是通過查詢發現,本地記錄和鏈上記錄是相吻合的,即無下注記錄。

(5)查詢 attackproxy1 的帳號記錄

慢霧:破解造成 BetDice 項目恐慌的交易回滾攻擊手法

可以看到的是,這個也與鏈上記錄吻合,只有開獎記錄,就像 tobetadmio 故意給 attackproxy1 開獎一般。

通過以上的復現及和鏈上記錄的對比,我們可以證明上文說的攻擊手法,就是黑客本次進行攻擊的手法,採用的就是使用黑名單進行回滾的操作。

防禦建議

1、針對 DApp 的防禦建議

(1)節點開啓 read only 模式,防止節點服務器上出現未確認的塊
(2)建立開獎依賴,如訂單依賴,開獎的時候判斷訂單是否存在,就算在節點服務器上開獎成功,由於在 bp 上下注訂單被回滾,所以相應的開獎記錄也會被回滾。

2、針對交易所和中心化錢包的防禦建議

慢霧安全團隊建議 EOS 交易所及中心化錢包在通過 RPC 接口 get_actions 查詢熱錢包充值記錄時,應檢查充值 transaction 所在的 block_num 是否小於 last_irreversible_block (最新不可逆區塊),如果 block_num 大於 last_irreversible_block 則表示該區塊仍然是可逆的,存在「假充值」風險。