EOS中的“鈔級騙局”:偽EOS及其變種攻擊
針對區塊鏈安全問題,Beosin(成都鏈安)團隊每一週都將出智慧合約安全漏洞解析連載,希望能幫助程式員寫出更加安全牢固的合約,防患於未然。
引子:寓目暫為實,過者即為虛。—— 《儼上人粹隱堂》 宋·梅堯臣
前情提要
上回書,EOS遊戲屢遭毒手,隨機數漏洞屢禁不絕。
當下正值各種EOS DApp火爆的時期,許多專案方紛紛開發自己的EOS遊戲吸引玩家。由於EOS官方並沒有提供隨機數介面,EOS遊戲在隨機數生成問題上面臨和以太坊遊戲一樣的窘境,開發者無法確保隨機數演算法中的因子是不受其他因素干擾的完全隨機因子,因此EOS遊戲隨機數攻擊一而再,再而三的發生,專案方也採取修改相關演算法的措施應對類似風險,如何做到正確修改,就要看開發者對隨機數因子的生成機制是否瞭解透徹,必要時還是需要藉助第三方審計,做到隨機數問題上的萬無一失。
本期話題
DApp假幣魚目混珠,轉賬函式檢測疏漏馬虎
說起今年比較賣座的國產電影,周潤發的《無雙》肯定佔有一席之地,電影中美元假鈔的製造技術讓人驚歎,也演繹了一段殘酷的“鈔級騙局”。
鈔票不夠用,就想到自己製造印刷,以假亂真,但是像影片中那種精湛的技藝確實還是很少的。在利益面前,任何一種貨幣都有被偽造的風險。對於EOS也不例外,本期我們就來說說由偽EOS攻擊引出的相關漏洞及其變體。
基礎知識鋪墊
EOS智慧合約的架構
EOS智慧合約是由一系列action組成的,每個action代表一條合約條款,以實現條款中的具體規則。執行一個EOS智慧合約分為製作、部署、呼叫三個部分。
其中,在部署智慧合約這一步,每個EOS智慧合約必須實現一個apply()函式,作用是將action請求對映到具體的處理函式。具體的實現細節封裝在EOSIO_ABI巨集裡面。這樣的設計讓開發者只需要專注業務邏輯的開發,而不必關注底層技術,起到簡化開發的作用。apply函式示例:
偽EOS轉賬攻擊
歷史事件:
大名鼎鼎的EOSBet在9月12日損失約42,000 EOS,開始了黑客使用偽EOS token攻擊的熱潮,接下來就是eoswindice和去中心化交易所newdex,都淪落為黑客的“魚肉”。
攻擊分析:
在EOS鏈上通用代幣為EOS token,EOS token是由EOSIO部署的eosio.token合約生成的,但是eosio.token程式碼是開源的,任何eos賬戶都可以自己部署eosio.token合約,並且發行縮寫名為EOS的token,如圖我們可以看到大量無價值的偽EOS:
如何鑑別真偽EOS呢?驗鈔機嗎?當然不用, eosflare瀏覽器上會顯示token的全名,真EOS和假EOS全名是有區別的:
從圖中可以看到 一個是EOS且後面沒有其他說明,另一個是refundwallet偽造的EOS (refundwallet)。
在瀏覽器中,我們可以看到交易細節來判斷用來交易的EOS token是真是假,那麼如果放在智慧合約中呢?
比如案例合約:
void transfer(const account_name& from,
const account_name& to,
const asset& quantity,
const string& memo)`
這是一個用來接收回調的函式,在被傳遞假EOS時,函式是沒有辦法分辨真偽的。如果沒有提前在apply中檢測EOS真假,就會導致合約接收了假的EOS也會執行正常業務邏輯。如果惡意賬戶自己部署合約釋出一個EOS token,就可以實現空手套白狼,用假鈔換真錢。
漏洞修復:
為了驗證EOS的發行方為eosio,需要在apply()中新增code == eosio.token進行判定。
還有一種修復方法,開發者可以使用extended_asset型別接收asset 然後呼叫其中的contract可以獲取到token的合約資訊,extended_asset quantity然後可以對quantity.contract驗證。
變體1:對transfer函式的直接呼叫攻擊
漏洞分析:
按照上述漏洞修復手段新增相關判定後,許多專案方長吁了一口氣,但是引發EOSBet事件的漏洞程式碼並沒有那麼簡單就被修復。因為,在使用上述的apply對code呼叫進行檢測時,還存在另一種繞過的情況,檢測條件如果只為:
if( code == self || code==N(eosio.token) ||action == N(onerror))
那麼這個檢測條件只處理兩個檢測:函式自身action的呼叫和來自eosio.token的action的呼叫,沒有校驗transfer action的呼叫方必須是eosio.token或自己的合約。
這樣就會導致可以直接呼叫合約賬戶下的transfer。
比如,原本是使用者A呼叫eosio.token給使用者B轉1 EOS,然後eosio.token傳送trasfer回執呼叫到B合約transfer函式執行業務邏輯,現在的話就可以直接呼叫B合約的transfer函式,裡面指定from,to,value等正確的引數,不通過轉賬直接執行到B合約的業務邏輯。所以進攻EOSBet的攻擊者完全繞過eosio.token->transfer函式,直接以正確的引數呼叫eosbetdice11->transfer,在不將EOS轉移到合約的情況下執行了合約的業務邏輯。
漏洞修復:
對於這一類攻擊的防禦方式主要是同時對action和code做檢測,即當
(code == N(eosio.token) && action == N(transfer)時才執行transfer函式。形式如下圖:
if (code == N(eosio.token) && action == N(transfer))
{ }
變體2 EOS假回執攻擊
歷史事件:
在9月14日的事件之後,EOSBet還發過官方宣告提到他們對程式碼安全的重視程度。
但是就在一個月之後,10月15日,EOSBet又被黑客攻擊,損失近14萬EOS。
漏洞分析:
漏洞原因是智慧合約處理邏輯的transfer函式內缺少對to的判斷:如果缺少to判斷,合約無法判斷收到轉賬的是不是自己,可能在沒有收到轉賬的情況下繼續執行邏輯功能,假設攻擊者擁有AB兩個賬戶,c是遊戲合約賬戶,攻擊者可以通過A賬戶呼叫eosio.token轉賬EOS到B賬戶,然後在A或B賬戶部署合約,在回撥transfer中再呼叫一次require_recipient(N(XXXXXX));將轉賬通知傳送到遊戲合約C賬戶,這樣就可以繞過code==N(eosio.token)&&action==transfer的校驗,執行相應的業務邏輯。
漏洞修復:
void transfer(const account_name& from,
const account_name& to,
const asset& quantity,
const string& memo) {
if (from == _self || to != _self) {//不能缺少to != _self判斷
return;
}
程式碼安全非黑即白
受到攻擊的遊戲官方經過一系列的安全事件、程式碼修復才最終解決了一個看似很匪夷所思的漏洞。可以看出,區塊鏈安全是一個異常嚴謹、值得考究的技術,近兩年的磕磕碰碰又讓它成為了一個非常沉重的話題。《無雙》中有句臺詞,“只看到黑跟白的人,永遠都是失敗者。”這句話在區塊鏈領域並不適用,不信你看電影主角的結局。
區塊鏈專案需要做到程式碼層面上的邏輯嚴密,設計精巧。區塊鏈安全規範和相關知識也需要理解透徹,融會貫通,在細節問題上的把握也異常重要,尤其是對於起步不久的EOS、Fabric等新平臺,所以可以說區塊鏈技術在程式碼安全方面非黑即白,做到安全開發、安全審計、安全部署,才能讓攻擊者、投機者無從下手。
引用:
[1]: 假EOS攻擊再升級:EOSCast遭黑客“假EOS轉賬變種”攻擊,損失超6萬EOS ofollow,noindex" target="_blank">https://www.huoxing24.com/newsdetail/20181101120201461288
[2]: BET被黑客攻擊始末,實錘還原作案現場和攻擊手段 https://bihu.com/article/1358201
[3]: 震驚!EOSBet又被攻擊了。損失高達500萬。攻擊手法竟是這樣? https://bihu.com/article/1558677
[4]: How EOSBET attacked by aabbccddeefg https://www.reddit.com/r/eos/comments/9fpcik/how_eosbet_attacked_by_aabbccddeefg/
[5]: EOSBet Transfer Hack Statement
https://medium.com/@eosbetcasino/eosbet-transfer-hack-statement-31a3be4f5dcf