1. 程式人生 > >【漏洞預警】首個區塊鏈token的自動化薅羊毛攻擊分析

【漏洞預警】首個區塊鏈token的自動化薅羊毛攻擊分析

事件介紹

近期,啟明星辰ADLab聯合電子科技大學的陳廳副教授追蹤到了以太坊token中的首個自動化薅羊毛攻擊事件。被攻擊的合約地址為0x86c8bf8532aa2601151c9dbbf4e4c4804e042571,token名稱為Simoleon (SIM),截止目前有接近57萬賬戶持有該合約的token:

經過深入分析,我們發現攻擊者對以太坊的理解非常深刻,其通過部署攻擊合約獲得了超過700萬的token,從57萬賬戶中脫穎而出,一舉成為該合約token的第四大持有者:

被攻擊的合約Simoleon

在token發行過程中,為增加人氣發行方可能會選擇空投,即在一定時間視窗和投放總量的條件下免費給參與地址傳送一定數量的token。Simoleon合約也加入了空投能力,在總量發放完之前,任何未接受過該token空投的賬戶都可以呼叫本合約的transfer函式給指定賬戶地址免費轉賬token:

攻擊過程分析

我們分析攻擊賬戶的token來源,通過etherscan檢視該賬戶的token交易記錄,一共分為兩項:709萬和1萬,分別來自不同的賬戶地址:

繼續跟蹤709萬token的交易記錄,其來源有三項:550萬,155萬和3萬,都來自同一個賬戶地址0x73fa30b609249166fa425f34b7142591c92b5548:

分析發現地址0x73fa30b609249166fa425f34b7142591c92b5548是一個閉源智慧合約,由賬戶0x0075fd99426911b2e9650114bedc1195ad027f21建立,該合約總共有23筆交易:

Simoleon合約對已經發放token的地址都進行了記錄,因此每個地址只能免費獲得1萬的token。要獲得更多的數量,就需要建立很多新賬戶,然後利用這些新賬戶呼叫Simoleon合約的transfer函式給指定賬戶來獲得大量token。如果通過建立EOA賬戶來進行薅羊毛,則每個賬戶都需要一些以太幣,否則EOA賬戶就沒有gas去呼叫Simoleon合約的transfer函式,也就無法薅羊毛。顯然,給大量EOA賬戶傳送以太幣會增加額外的交易手續費。

攻擊者在這裡採用了一種精妙的攻擊手法:首先部署一個惡意的攻擊合約,地址為0x73fa30b609249166fa425f34b7142591c92b5548;然後呼叫攻擊合約中籤名為0x2b6cab44的函式來動態生成臨時合約;最後臨時合約在建構函式中直接呼叫Simoleon合約的transfer函式給攻擊合約傳送token。因為動態生成的臨時合約都是未領取過token的賬戶,所以都能自動獲得空投份額。攻擊者只需要給攻擊賬戶傳送以太幣就有足夠的gas呼叫攻擊合約,同時攻擊者還能控制臨時合約的生成數量。

通過分析攻擊合約的23個交易資料,得到攻擊者對0x2b6cab44函式的呼叫過程如下:

其中第一筆的交易(0x7f87268e06b76644ad4b8e162fb76d1c6e2e0480029b14db0e013f7d7d195e2f)細節如下,攻擊者通過0x2b6cab44函式的引數來控制只生成2個臨時合約,2個臨時合約分別獲取1萬的token,然後傳送給攻擊合約。臨時合約最後還進行了自動銷燬,使自己的程式碼從區塊鏈上消失。

攻擊者前面4筆的交易中,每次生成的臨時合約數量越來越大。其中第4次生成了80個臨時合約,但是執行失敗了,檢視原因是gas不足。

攻擊者在後續的交易中,每次都選擇只生成50個臨時合約。理論上攻擊者可以增大允許的gas消耗量,從而一次性建立80個臨時合約來加快薅羊毛,但攻擊者卻選擇一次50個的搬磚模式。通過深入分析,我們發現攻擊者對以太坊的底層機制相當熟悉,比如這裡的gas消耗問題。

在以太坊中,每一筆交易都會消耗一定量的gas,實際消耗量由交易的複雜度決定,但不會超過傳送者指定該交易能允許的最大消耗量。逆向分析0x2b6cab44函式後發現它是在迴圈建立臨時合約,顯然迴圈次數越多,這個函式呼叫的交易複雜度就越高,實際的gas消耗量就越大,允許的gas消耗量也就必須設定為越大。臨時合約數量設定為50的時候,gas實際消耗量為3096224;臨時合約數量設定為80時,gas實際消耗量為6174290。

然而,每個區塊又有最大gas消耗量的閾值,並且每個區塊至少要容納1筆交易,因此以太坊鏈上任何一筆交易的gas消耗量都不允許超過區塊的閾值。這進一步導致,在礦工最優化收益方案中,如果一個交易允許的gas消耗量過大,就會傾向性把這個交易排除在區塊外,從而導致該交易失敗。攻擊者通過探測嘗試找到最佳臨時合約數量為50(因為80個臨時合約的gas消耗已經超過允許的gas消耗導致交易失敗),從而實現一次呼叫可以薅更多token,同時又不會觸發gas問題。

行家一出手,就知有沒有!不愧是有知識的攻擊者。

暗藏玄機的etherscan

在追蹤分析過程中,我們發現了另外一起問題,即etherscan對token的變化記錄是不準確的,甚至可能完全是錯誤的。如下是攻擊合約的第一筆token收入,2個臨時合約總共獲得2萬token,然後傳送給了攻擊合約:

緊接著攻擊合約產生了第一筆token支出,傳送了3萬token給攻擊者:

根據etherscan中的交易記錄,攻擊合約的收入只有2萬,結果支援了3萬。多支出的1萬出自哪裡,是否憑空產生?為何這1萬的變化,etherscan沒有任何記錄? 

經過分析,找到原因就在Simoleon合約的transfer函式,合約在空投的時候也會對目標地址進行免費傳送token,但是這個發放卻沒有呼叫Transfer事件:

於是,攻擊合約的token變化過程如下:

(1)攻擊合約被建立,token為零。

(2)攻擊合約0x2b6cab44函式第一次被呼叫,建立2個臨時合約:

  • 第1個臨時合約初始化時呼叫Simoleon的transfer函式,該函式首先給臨時合約空投initialize(msg.sender),此時臨時合約的token為1萬;然後給攻擊合約空投initialize(_to),此時攻擊合約的token為1萬;最後把臨時合約的1萬token傳送給攻擊合約,同時呼叫事件Transfer記錄臨時合約給攻擊合約的傳送量為1萬。至此,攻擊合約的token總計為2萬,由空投的1萬加上臨時合約傳送的1萬構成。

  • 第2個臨時合約初始化時呼叫Simoleon的transfer函式,該函式首先給臨時合約空投initialize(msg.sender),此時臨時合約擁有的token為1萬;然後把臨時合約的1萬token傳送給攻擊合約,同時呼叫事件Transfer記錄臨時合約給攻擊合約的傳送量為1萬。至此,攻擊合約的token總計為3萬,由先前2萬加臨時合約傳送的1萬構成。

(3)攻擊者從攻擊合約提取全部token,提取總量token為3萬。

因此,從攻擊合約賬戶自身的收支平衡來看,並沒有憑空產生token,只是etherscan未能監控到攻擊合約因為空投獲得的1萬token。這說明了etherscan對token變化的監控是基於Transfer事件,而不是基於每個賬戶的實際token變化。這會帶來一些潛在的威脅,因為Transfer事件在本質上只是一個道德約束,EVM本身對其沒有任何限制能力,攻擊者可以不呼叫事件,也可以任意修改該事件的引數,實現諸如:實際轉移的token量比事件通知的數量不匹配。對於頻繁交易token的節點來說,這是一個新的挑戰,因為該節點將難以通過鏈上資料追蹤每筆token的來源,因為Transfer事件不再是準確的。

總 結

Simoleon合約能被自動化的薅羊毛,最根本的原因在於其合約程式碼的空投獎勵函式沒有任何許可權控制,任意未領過空投的賬戶都可以直接呼叫Simoleon合約來獲取。因此,攻擊者可以利用攻擊合約來建立大量賬戶來領取。一種緩解辦法是給空投獎勵函式設定許可權控制,比如只有合約建立者才能給目標地址發放token。 

區塊鏈的特性決定了合約程式碼一經部署就無法修改,無論是合約的開發者,還是投資者,還是交易平臺,都應當對合約進行多重安全審計,儘量在上鍊前消除安全風險。

參考連結:

【1】 Ting Chen, University of ElectronicScience and Technology of China. 

http://faculty. uestc.edu.cn/chenting/zh_CN/ index.htm

【2】 Simoleon (SIM). https://etherscan.io/address/0x86c8bf8532aa2601151c9dbbf4e4c4804e042571