作者:LoRexxar'@知道創宇404區塊鏈安全研究團隊
時間:2018年8月10日
英文版:http://www.bjnorthway.com/670/

一、 簡介

在知道創宇404區塊鏈安全研究團隊整理輸出的《知道創宇以太坊合約審計CheckList》中,把“未觸發Transfer事件問題”、“未觸發Approval事件問題”、“假充值漏洞”、“構造函數書寫錯誤”等問題統一歸類為“以太坊智能合約規范問題”。

“昊天塔(HaoTian)”是知道創宇404區塊鏈安全研究團隊獨立開發的用于監控、掃描、分析、審計區塊鏈智能合約安全自動化平臺。我們利用該平臺針對上述提到的《知道創宇以太坊合約審計CheckList》中“以太坊智能合約規范”類問題在全網公開的智能合約代碼做了掃描分析。詳見下文:

二、漏洞詳情

ERC20是一種代幣標準,用于以太坊區塊鏈上的智能合約。ERC20定義了一種以太坊必須執行的通用規則,如果在以太坊發行的代幣符合ERC20的標準,那么交易所就可以進行集成,在它們的交易所實現代幣的買賣和交易。

ERC20中規定了transfer函數必須觸發Transfer事件,transfer函數必須返回bool值,在進行余額判斷時,應拋出錯誤而不是簡單的返回錯誤,approve函數必須觸發Approval事件。

1、未觸發Transfer事件

function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);           
        require(balanceOf[_to] + _value >= balanceOf[_to]); 
        balanceOf[msg.sender] -= _value;                            
        balanceOf[_to] += _value;                           
        return true;
    }

上述代碼在發生交易時未觸發Transfer事件,在發生交易時,未產生event事件,不符合ERC20標準,不便于開發人員對合約交易情況進行監控。

2、未觸發Approval事件

    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

上述代碼在發生交易時未觸發Approval事件,在發生交易時,未產生event事件,不符合ERC20標準,不便于開發人員對合約情況進行監控。

3、假充值漏洞

    function transfer(address _to, uint256 _amount) returns (bool success) {
        initialize(msg.sender);

        if (balances[msg.sender] >= _amount
            && _amount > 0) {
            initialize(_to);
            if (balances[_to] + _amount > balances[_to]) {

                balances[msg.sender] -= _amount;
                balances[_to] += _amount;

                Transfer(msg.sender, _to, _amount);

                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

上述代碼在判斷余額時使用了if語句,ERC20標準規定,當余額不足時,合約應拋出錯誤使交易回滾,而不是簡單的返回false。

這種情況下,會導致即使沒有真正發生交易,但交易仍然成功,這種情況會影響交易平臺的判斷結果,可能導致假充值。

2018年7月9日,慢霧安全團隊發布了關于假充值的漏洞預警。

2018年7月9日,知道創宇404區塊鏈安全研究團隊跟進應急該漏洞,并對此漏洞發出了漏洞預警。

4、構造函數書寫錯誤漏洞

Solidity0.4.22版本以前,編譯器要求,構造函數名稱應該和合約名稱保持一致,如果構造函數名字和合約名字大小寫不一致,該函數仍然會被當成普通函數,可以被任意用戶調用。

Solidity0.4.22中引入了關于構造函數constructor使用不當的問題,constructor在使用中錯誤的加上了function定義,從而導致constructor可以被任意用戶調用,會導致可能的更嚴重的危害,如Owner權限被盜。

構造函數大小寫錯誤漏洞
contract own(){
    function Own() {
        owner = msg.sender;
    }
}

上述代碼錯誤的將構造函數名大寫,導致構造函數名和合約名不一致。這種情況下,該函數被設置為一個普通的public函數,任意用戶都可以通過調用該函數來修改自己為合約owner。進一步導致其他嚴重的后果。

2018年6月22日,MorphToken合約代幣宣布更新新的智能合約,其中修復了關于大小寫錯誤導致的構造函數問題。

2018年6月22日,知道創宇404區塊鏈安全研究團隊跟進應急,并輸出了《以太坊智能合約構造函數編碼錯誤導致非法合約所有權轉移報告》。

構造函數編碼錯誤漏洞
    function constructor() public {
        owner = msg.sender;
    }

上述代碼錯誤的使用function來作為constructor函數裝飾詞,這種情況下,該函數被設置為一個普通的public函數,任意用戶都可以通過調用該函數來修改自己為合約owner。進一步導致其他嚴重的后果。

2018年7月14日,鏈安科技在公眾號公布了關于constructor函數書寫錯誤的問題詳情

2018年7月15日,知道創宇404區塊鏈安全研究團隊跟進應急,并輸出了《以太坊智能合約構造函數書寫錯誤導致非法合約所有權轉移報告》

三、漏洞影響范圍

使用Haotian平臺智能合約審計功能可以準確掃描到該類型問題。

基于Haotian平臺智能合約審計功能規則,我們對全網的公開的共39548 個合約代碼進行了掃描,其中共14978個合約涉及到這類問題。

1、 未觸發Transfer事件

截止2018年8月10日為止,我們發現了4604個存在未遵循ERC20標準未觸發Transfer事件的合約代碼,其中交易量最高的10個合約情況如下:

2、 未觸發Approval事件

截止2018年8月10日為止,我們發現了5231個存在未遵循ERC20標準未出發Approval事件的合約代碼,其中交易量最高的10個合約情況如下:

3、假充值漏洞

2018年7月9日,知道創宇404區塊鏈安全研究團隊在跟進應急假充值漏洞時,曾對全網公開合約代碼進行過一次掃描,當時發現約3141余個存在假充值問題的合約代碼,其中交易量最高的10個合約情況如下:

截止2018年8月10日為止,我們發現了5027個存在假充值問題的合約代碼,其中交易量最高的10個合約情況如下:

4、構造函數書寫問題

構造函數大小寫錯誤漏洞

2018年6月22日,知道創宇404區塊鏈安全研究團隊在跟進應急假充值漏洞時,全網中存在該問題的合約約為16個。

截止2018年8月10日為止,我們發現了90個存構造函數大小寫錯誤漏洞的合約代碼,其中交易量最高的10個合約情況如下:

構造函數編碼問題

截止2018年8月10日為止,我們發現了24個存在構造函數書寫問題的合約代碼,比2018年7月14日對該漏洞應急時只多了一個合約,其中交易量最高的10個合約情況如下:

四、修復方式

1)transfer函數中應觸發Tranfser事件

  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_value <= balances[msg.sender]);
    require(_to != address(0));

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

2)approve函數中應觸發Approval事件

  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    emit Approval(msg.sender, _spender, _value);
    return true;
  }

3)transfer余額驗證時應使用require拋出錯誤

  function transfer(address _to, uint256 _value) public returns (bool) {
    require(_value <= balances[msg.sender]);
    require(_to != address(0));

    balances[msg.sender] = balances[msg.sender].sub(_value);
    balances[_to] = balances[_to].add(_value);
    emit Transfer(msg.sender, _to, _value);
    return true;
  }

4)0.4.22版本之前,構造函數應和合約名稱一致

contract ownable {
    function ownable() public {
    owner = msg.sender;
  }

5)0.4.22版本之后,構造函數不應用function修飾

constructor() public {
        owner = msg.sender;
    }

五、一些思考

上面這些問題算是我在回顧歷史漏洞中經常發現的一類問題,都屬于開發人員沒有遵守ERC20標準而導致的,雖然這些問題往往不會直接導致合約漏洞的產生,但卻因為這些沒有遵守標準的問題,在后期對于合約代幣的維護時,會出現很多問題。

如果沒有在transfer和approve時觸發相應的事件,開發人員就需要更復雜的方式監控合約的交易情況,一旦發生大規模盜幣時間,甚至沒有足夠的日志提供回滾。

如果轉賬時沒有拋出錯誤,就有可能導致假充值漏洞,如果平臺方在檢查交易結果時是通過交易狀態來判斷的,就會導致平臺利益被損害。

如果開發人員在構造函數時,沒有注意不同版本的編譯器標準,就可能導致合約所有權被輕易盜取,導致進一步更嚴重的盜幣等問題。

我們在對全網公開的合約代碼進行掃描和監控時容易發現,有很大一批開發人員并沒有注意到這些問題,甚至構造函數書寫錯誤這種低級錯誤,在漏洞預警之后仍然在發生,考慮到大部分合約代碼沒有公開,可能還有很多開發者在不遵守標準的情況下進行開發,還有很多潛在的問題需要去考慮。

這里我們建議所有的開發者重新審視自己的合約代碼,檢查是否遵守了ERC20合約標準,避免不必要的麻煩以及安全問題。


智能合約審計服務

針對目前主流的以太坊應用,知道創宇提供專業權威的智能合約審計服務,規避因合約安全問題導致的財產損失,為各類以太坊應用安全保駕護航。

知道創宇404智能合約安全審計團隊: https://www.scanv.com/lca/index.html
聯系電話:(086) 136 8133 5016(沈經理,工作日:10:00-18:00)

歡迎掃碼咨詢:

區塊鏈行業安全解決方案

黑客通過DDoS攻擊、CC攻擊、系統漏洞、代碼漏洞、業務流程漏洞、API-Key漏洞等進行攻擊和入侵,給區塊鏈項目的管理運營團隊及用戶造成巨大的經濟損失。知道創宇十余年安全經驗,憑借多重防護+云端大數據技術,為區塊鏈應用提供專屬安全解決方案。

歡迎掃碼咨詢:


六、REF

[1] ERC標準
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md

[2] Morpheus官方公告
https://medium.com/@themorpheus/new-morpheus-network-token-smart-contract-91 b80dbc7655

[3] 構造函數書寫問題漏洞詳情
https://mp.weixin.qq.com/s/xPwhanev-cjHhc104Wmpug


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/663/