引子:橫看成嶺側成峰,遠近高低各不同。不識廬山真面目,只緣身在此山中。
—— 《題西林壁》蘇軾
智慧型合約作為以太坊上各種加密數字貨幣的基礎,承載著巨大的經濟利益。放眼以太坊的繁忙景象,日交易頻率在50-75萬次之間,而五月和二月更有兩日交易次數高達100萬次。合約的呼叫在整個以太坊生態系統猶如戰場上穿梭的子彈,不計其數。
可見其地位舉足輕重,功能五花八門,但萬變不離其宗的是,每個合約都有乙個共性,會繼承乙個位址物件,也就是說合約的基礎是位址。回顧前四期漏洞分析,我們了解攻擊者使出了渾身解數從合約的各個部分試圖不勞而獲,而從合約的位址漏洞入手,釜底抽薪自然也會成為攻擊手段之一。
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]。
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
以太坊RLP機制分析
目錄 1 rlp 定義 2 rlp 編碼規則 3 rlp 編碼例項 4 rlp 分析 1 rlp 定義 rlp,即 recursive length prefix,遞迴長度字首編碼,是以太坊資料序列化的主要方法,具有較好的資料處理效率,尤其是將長度和型別統一作為字首,實際上 rlp 是基於 asci...
以太坊 RLP機制分析
目錄 1 rlp 定義 2 rlp 編碼規則 3 rlp 編碼例項 4 rlp 分析 1 rlp 定義 rlp,即 recursive length prefix,遞迴長度字首編碼,是以太坊資料序列化的主要方法,具有較好的資料處理效率,尤其是將長度和型別統一作為字首,實際上 rlp 是基於 asci...
以太坊RLP機制分析
目錄 1 rlp 定義 2 rlp 編碼規則 3 rlp 編碼例項 4 rlp 分析 1 rlp 定義 rlp,即 recursive length prefix,遞迴長度字首編碼,是以太坊資料序列化的主要方法,具有較好的資料處理效率,尤其是將長度和型別統一作為字首,實際上 rlp 是基於 asci...