1. 程式人生 > >確認過眼神,地址不是對的人 | 成都鏈安漏洞分析連載第五期 —— 許可權驗證錯誤

確認過眼神,地址不是對的人 | 成都鏈安漏洞分析連載第五期 —— 許可權驗證錯誤

針對區塊鏈安全問題,成都鏈安科技團隊每一週都將出智慧合約安全漏洞解析連載,希望能幫助程式設計師寫出更加安全牢固的合約,防患於未然。

引子:橫看成嶺側成峰,遠近高低各不同。不識廬山真面目,只緣身在此山中。

—— 《題西林壁》蘇軾

上回說到

底層函式呼叫險象環生

外部功能慎用防患未然

真假難辨黑客詭計多端

完善規則杜絕內憂外患

本回

地址恢復功虧一簣

身份判斷輕慮淺謀

邏輯補全自圓其說

原理辨析帷幄運籌

智慧合約作為以太坊上各種加密數字貨幣的基礎,承載著巨大的經濟利益。放眼以太坊的繁忙景象,日交易頻率在50-75萬次之間,而五月和二月更有兩日交易次數高達100萬次。合約的呼叫在整個以太坊生態系統猶如戰場上穿梭的子彈,不計其數。

可見其地位舉足輕重,功能五花八門,但萬變不離其宗的是,每個合約都有一個共性,會繼承一個地址物件,也就是說合約的基礎是地址。回顧前四期漏洞分析,我們瞭解攻擊者使出了渾身解數從合約的各個部分試圖不勞而獲,而從合約的地址漏洞入手,釜底抽薪自然也會成為攻擊手段之一。

下面我們就來聊聊,與地址有關的tx.origin變數和ecrecover()函式的相關漏洞。

一言以蔽之

Solidity作為以太坊的官方語言,將地址定為20個位元組,160位。所有合約都有一個地址物件,也可以對其他地址的程式碼進行呼叫。地址型別的成員有我們上一期講到的call()和delegatecall(),這兩個函式相關的漏洞請見右方連結:

偷天換日合約易主,地址變臉移花接木 |成都鏈安漏洞分析連載第四期 —— 底層函式誤用漏洞。不同的合約地址代表了不同的合約,也代表了他們所擁有的許可權。所以合約地址就相當於合約的“身份證”。

本期相關兩個名詞專業的解釋是, tx.origin是Solidity的一個全域性變數,它遍歷整個呼叫棧並返回最初發送呼叫(或事務)的帳戶的地址。ecrecover()是內嵌的函式,可以用來恢復簽名公鑰,傳值正確的情況下,可以利用這個函式來驗證地址。

通俗來說,tx.origin是整個交易過程最初的那個合約擁有者。當這個合約A直接呼叫合約B的時候,擁有者也是中間人沒錯,都是合約A。

但是當中間多出了一個合約C之後,合約A呼叫合約C,合約C呼叫合約B,這時中間人是C,但是tx.origin是合約A。

利用tx.origin的漏洞可以比喻為:

小明未滿十八歲,但對手中的遊戲愛不釋手,但是遊戲方為了限制未成年人的遊戲時間,只有身份證號驗證已滿十八週歲的玩家才能無限制暢玩。於是小明使用長輩的身份證號進行防沉迷驗證。這樣,本來是遊戲方對小明身份的驗證,變成了對其長輩身份的驗證,驗證結果予以通過,給予小明他不應有的許可權和利益。

至於ecrecover函式的問題,上面說到傳值正確的情況下,使用是沒有問題的,但普遍存在的問題是,傳值如果異常,沒有進行相應的判定和排除。

我們來具體分析案例進行程式碼層面的剖析。

深度分析

1.   tx.origin使用錯誤

漏洞分析 

tx.origin是Solidity的一個全域性變數,它遍歷整個呼叫棧並返回最初發送呼叫(或事務)的帳戶的地址。在智慧合約中使用此變數進行身份驗證會使合約容易受到類似網路釣魚的攻擊。有關進一步閱讀,請參閱StackExchangeQuestion, PeterVenesses部落格和Solidity-tx.origin攻擊。

對如下案例合約進行分析:

該合約有三個函式:constructor建構函式,指定合約owner;fallback函式,通過新增payable關鍵字以便接收使用者轉賬;withdrawAll函式,對tx.origin進行判斷,如果tx.origin是owner,則將合約地址所擁有的ether傳送到_recipient中。

現在,一個攻擊者建立了下列合約:

攻擊者誘使原合約(Phishable.sol)的owner傳送ether到攻擊合約(POC.sol)地址,然後呼叫攻擊合約的fallback函式,執行attack()函式,此時phOwner == msg.sender,將會呼叫原合約的withdrawAll()函式,程式執行進入原合約,此時msg.sender是攻擊合約的地址,tx.origin是最初發起交易的地址,即原合約的owner,require(tx.origin == owner);條件滿足,_recipient.transfer(this.balance);可以執行,即將原合約地址裡的ether轉給攻擊者。

漏洞修復

tx.origin不應該用於智慧合約的授權。這並不是說永遠不應該使用tx.origin變數。它在智慧合約中確實有一些合法的用例。例如,如果想要拒絕外部合約呼叫當前合約,他們可以通過require(tx.origin ==msg.sender)實現。 這可以防止使用中間合約來呼叫當前合約[1]。

·   參考連結:

https://blog.sigmaprime.io/solidity-security.html#tx-origin

2. ecrecover 未作0地址判斷

漏洞分析

keccak256() 和  ecrecover()都是內嵌的函式, keccak256() 可以用於計算公鑰的簽名, ecrecover()可以用來恢復簽名公鑰。傳值正確的情況下,可以利用這兩個函式來驗證地址。

我們來看一個案例合約:

這個合約看似正常,但思索再三,如果ecrecover傳入錯誤引數(例如_v = 29,),函式返回0地址。如果合約函式傳入的校驗地址也為零地址,那麼將通過斷言,導致合約邏輯錯誤。

函式transferProxy中,如果傳入的引數_from為0,那麼ecrecover函式因為輸入引數錯誤而返回0值之後,if判斷將通過,從而導致合約漏洞[2]。

函式 decode()傳入經過簽名後的資料,用於驗證返回地址是否是之前用於簽名的私鑰對應的公鑰地址[3]。以太坊提供了web3.eth.sign方法來對資料生成數字簽名。上面的簽名資料可以通過下面的js程式碼獲得:

js程式碼執行結果如下:

漏洞修復

對0x0地址做過濾,例如:

參考資料

·    transferProxy-keccak256

·    approveProxy-keccak256

高枕無憂居安思危!

(此處劃去效果需要顯示,起到強調的效果)

細心的朋友會發現,我們這一期並沒有相應的事件回顧,那是因為早在2016年6月25日就有使用者在github上向Solidity官方反應存在tx.origin的概念混淆,並建議移除tx.origin這個變數。

事關地址許可權驗證,而且theDAO事件風波未平(發生於6月17日),大量的討論者參與了進來。究竟應不應該停止使用這個變數呢?我們在漏洞修復的環節中已經提到,如果想要拒絕外部合約呼叫當前合約,可以通過require(tx.origin ==msg.sender)實現。 

這可以防止使用中間合約來呼叫當前合約,這可以防止外部函式呼叫時產生的風險,我們在上一期已經討論過相關的漏洞,詳情請見右方連結:偷天換日合約易主,地址變臉移花接木 |成都鏈安漏洞分析連載第四期 —— 底層函式誤用漏洞

於是,有使用者新提出了一個使用tx.origin產生警告的建議。

官方警惕性非常高,及時處理了這個問題,於是這個漏洞暫時還沒有被利用於惡性攻擊的歷史事件。

那是不是可以說,這個漏洞並不重要呢?

目前合約的發展趨勢正在發生變化,重心漸漸向以太坊遊戲傾斜。以太坊遊戲以娛樂和休閒為外衣,對數字貨幣的交易進行了整合和包裝。但是遊戲智慧合約的漏洞依然層出不窮,不但有新型的非合約漏洞,而且還存在已經報道出的一些漏洞。更有甚者,故意暴露出看似明顯的合約或機制缺陷,引誘投機倒把的玩家或者黑客,但暗中附加套路,玩起了“螳螂捕蟬黃雀在後”。

這種新型的詐騙合約我們稱為“蜜罐”。一些已知的缺陷通過黑客的修飾搖身一變,成為新型的騙局合約,例如近期有相關安全公司報道的名為QUESTION (中文含義:問題)的遊戲合約,利用的是etherscan.io的缺陷,明修棧道,暗度陳倉,捲走玩家的錢財。

面幣思過

無論是地址驗證的欺騙,還是蜜罐合約的引誘,不懷好意的人總是瞄準了深陷幣圈投資者的口袋。成都鏈安團隊提醒玩家和投資者們提高安全意識,以及對智慧合約的瞭解程度,切不可盲目跟風。我們從這個早期漏洞可以受到的啟發是,要辨析一些容易混淆的原理之後,才能認清某些不易察覺的陷阱和漏洞,提高警惕,理智投資,穩健操作,會當凌絕頂,一覽眾山小。

引用:

[1]:  

Solidity Security

https://blog.sigmaprime.io/solidity-security.html#tx-origin

[2]: 

ERC20 Token 合約安全風險問題彙總transferProxy-keccak256

[3]:  

ERC20 Token 合約安全風險問題彙總approveProxy-keccak256

相關閱讀: