以太坊合約審計 CheckList 之“以太坊智能合約編碼隱患”影響分析報告

一、簡 介

在知道創宇404區塊鏈安全研究團隊整理輸出的《知道創宇以太坊合約審計CheckList》中,我們把超過10個問題點歸結爲開發者容易忽略的問題隱患,其中包括“語法特性”、“數據私密性”、“數據可靠性”、“gas消耗優化”、“合約用戶”、“日誌記錄”、“回調函數”、“Owner權限”、“用戶鑑權”、 “條件競爭”等,統一歸類爲“以太坊智能合約編碼隱患”。

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

二、漏洞詳情

以太坊智能合約是以太坊概念中非常重要的一個概念,以太坊實現了基於solidity語言的以太坊虛擬機(Ethereum Virtual Machine),它允許用戶在鏈上部署智能合約代碼,通過智能合約可以完成人們想要的合約。

這次我們提到的問題多數屬於智能合約獨有問題,與我們常見的各類代碼不同,在編寫智能合約代碼時還需要考慮多種問題。

1、語法特性

在智能合約中小心整數除法的向下取整問題

在智能合約中,所有的整數除法都會向下取整到最接近的整數,當我們需要更高的精度時,我們需要使用乘數來加大這個數字。

該問題如果在代碼中顯式出現,編譯器會提出問題警告,無法繼續編譯,但如果隱式出現,將會採取向下取整的處理方式。

錯誤樣例

uint x = 5 / 2; // 2 正確代碼 uint multiplier = 10; uint x = (5 * multiplier) / 2;

2、數據私密性

在合約中,所有的數據都是公開的。包括私有變量等,不得將任何帶有私密性的數據儲存在鏈上。

3、數據可靠性

在合約中,許多開發者習慣用時間戳來做判斷條件,例如

uint someVariable = now + 1; if (now % 2 == 0) { // now可能被礦工控制 }

now、block_timestamp會被礦工所控制,並不可靠。

4、gas消耗優化

contract EUXLinkToken is ERC20 { using SafeMath for uint256; address owner = msg.sender; mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; mapping (address => bool) public blacklist; string public constant name = "xx"; string public constant symbol = "xxx"; uint public constant decimals = 8; uint256 public totalSupply = 1000000000e8; uint256 public totalDistributed = 200000000e8; uint256 public totalPurchase = 200000000e8; uint256 public totalRemaining = totalSupply.sub(totalDistributed).sub(totalPurchase); uint256 public value = 5000e8; uint256 public purchaseCardinal = 5000000e8; uint256 public minPurchase = 0.001e18; uint256 public maxPurchase = 10e18;

在合約中,涉及到狀態變化的代碼會消耗更多的,爲了經可能優化gas消耗,對於不涉及狀態變化的變量應該加constant來限制

5、合約用戶

合約中,交易目標可能爲合約,因此可能會產生的各種惡意利用。

contract Auction{ address public currentLeader; uint256 public hidghestBid; function bid() public payable { require(msg.value > highestBid); require(currentLeader.send(highestBid)); currentLeader = msg.sender; highestBid = currentLeader; } }

上述合約就是一個典型的沒有考慮合約爲用戶時的情況,這是一個簡單的競拍爭奪王位的代碼。當交易ether大於合約內的highestBid,當前用戶就會成爲合約當前的"王",他的交易額也會成爲新的highestBid。

contract Attack { function () { revert(); } function Attack(address _target) payable { _target.call.value(msg.value)(bytes4(keccak256("bid()"))); } }

但當新的用戶試圖成爲新的“王”時,當代碼執行到require(currentLeader.send(highestBid));時,合約中的fallback函數會觸發,如果攻擊者在fallback函數中加入revert()函數,那麼交易就會返回false,即永遠無法完成交易,那麼當前合約就會一直成爲合約當前的"王"。

6、日誌記錄

當合約跑在鏈上之後,鏈上的一切數據都難以監控,對於一個健康的智能合約來說,記錄合理的event,爲了便於運維監控,除了轉賬,授權等函數以外,其他操作也需要加入詳細的事件記錄,如轉移管理員權限、其他特殊的主功能。

fonction transferOwnership(address newOwner) onlyOwner public { ownner = newOwner; emit OwnershipTransferred(owner, newowner); }

7、回調函數

fallback機制是基於智能合約的特殊性而存在的。對於智能合約來說,任何函數的執行都是通過交易來完成的,但函數的執行過程中可能會遇到各種各樣的問題,在交易失敗或者交易結束後,就會執行fallback來最後處理結果和返回。

而在合約交易中,執行的每一個操作都會花費巨大的gas,如果gas不足,那麼fallback函數也會執行失敗。在evm中規定,交易失敗時,只有2300gas用於執行fallback函數,而2300gas只允許執行一組字節碼指令。一旦遇到極端情況,可能會因爲gas不夠用導致某種情況發生,導致未知的不可挽回的後果。

例如

function() payable { LogDepositReceived(msg.sender); } function() public payable{ revert();};

8、Owner權限

避免owner權限過大

部分合約owner權限過大,owner可以隨意操作合約內各種數據,包括修改規則,任意轉賬,任意鑄幣燒幣,一旦發生安全問題,可能會導致嚴重的結果。

function destroy() onlyOwner public onlyOwner{ selfdestruct(owner); }

9、用戶鑑權問題

合約中不要使用tx.origin做鑑權

tx.origin代表最初始的地址,如果用戶a通過合約b調用了合約c,對於合約c來說,tx.origin就是用戶a,而msg.sender纔是合約b,對於鑑權來說,這是十分危險的,這代表着可能導致的釣魚攻擊。

下面是一個範例:

pragma solidity >0.4.24; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract TxUserWallet { address owner; constructor() public { owner = msg.sender; } function transferTo(address dest, uint amount) public { require(tx.origin == owner); dest.transfer(amount); } }

我們可以構造攻擊合約

pragma solidity >0.4.24; interface TxUserWallet { function transferTo(address dest, uint amount) external; } contract TxAttackWallet { address owner; constructor() public { owner = msg.sender; } function() external { TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); } }

當用戶被欺騙調用攻擊合約,則會直接繞過鑑權而轉賬成功,這裏應使用msg.sender來做權限判斷。

https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin

10、條件競爭

在智能合約中,經常容易出現對交易順序的依賴,如佔山爲王規則、或最後一個贏家規則。都是對交易順序有比較強的依賴的設計規則,但以太坊本身的底層規則是基於礦工利益最大法則,在一定程度的極限情況下,只要攻擊者付出足夠的代價,他就可以一定程度控制交易的順序。開發者應避免這個問題。

真實世界事件:

智能合約遊戲之殤——類 Fomo3D 攻擊分析

三、漏洞影響範圍

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

基於Haotian平臺智能合約掃描功能規則,我們對全網的公開的共47305個合約代碼進行了掃描。

其中存在數據可靠問題的合約共2732個, 存在int型變量gas優化問題的合約共18285個, 存在string型變量gas優化問題的合約共194個, 存在Owner權限過大或合約後門的合約共1194個, 存在tx.origin 鑑權問題問題的合約共52個。

1、數據可靠性

截止2018年10月31日,我們發現了2732個存在數據可靠問題的合約代碼,存在潛在的安全隱患。其中交易量最高的10個合約情況如下:

2、gas消耗優化

截止2018年10月31日,我們發現了18285個存在int型變量gas優化問題的合約代碼,存在潛在的安全隱患。其中交易量最高的10個合約情況如下:

截止2018年10月31日,我們發現了194個存在string型變量gas優化問題的合約代碼,存在潛在的安全隱患。其中交易量最高的10個合約情況如下:

3、回調函數

截止2018年10月31日,我們發現了8321個存在複雜回調的合約代碼,存在潛在的安全隱患。其中交易量最高的10個合約情況如下:

4、Owner權限

截止2018年10月31日,我們發現了1194個存在Owner權限過大或合約後門,其中交易量最高的10個合約情況如下:

5、tx.origin 鑑權問題

截止2018年10月31日,我們發現了52個存在tx.origin 鑑權問題,其中交易量最高的10個合約情況如下:

四、修復方式

1、語法特性

在智能合約中小心整數除法的向下取整問題,可以通過先乘積爲整數再做處理。

uint multiplier = 10; uint x = (5 * multiplier) / 2;

2、數據私密問題

在處理一些隱私數據是儘量保留在服務端,可以通過hash-commit的方式來check變量值。

3、數據可靠性

儘量使合約內容不依賴時間順序,如果需要外部變量影響,那儘量採用block.height和block.hash等這類難以控制的變量。

4、gas消耗優化

對於某些不涉及狀態變化的函數和變量可以加constant來避免gas的消耗

5、合約用戶

合約中,應儘量考慮交易目標爲合約時的情況,避免因此產生的各種惡意利用。

6、日誌記錄

關鍵事件應有Event記錄,爲了便於運維監控,除了轉賬,授權等函數以外,其他操作也需要加入詳細的事件記錄,如轉移管理員權限、其他特殊的主功能。

fonction transferOwnership(address newOwner) onlyOwner public { ownner = newOwner; emit OwnershipTransferred(owner, newowner); }

7、回調函數

合約中定義Fallback函數,並使Fallback函數儘可能的簡單。儘量避免在回調函數中調用transfer、call等涉及狀態變化的操作,避免gas不夠用直接導致未知情況發生。

8、Owner權限問題

部分合約owner權限過大,owner可以隨意操作合約內各種數據,包括修改規則,任意轉賬,任意鑄幣燒幣,一旦發生安全問題,可能會導致嚴重的結果。

關於owner權限問題,應該遵循幾個要求:

  1. 合約創造後,任何人不能改變合約規則,包括規則參數大小等
  2. 只允許owner在合約銷燬前,從合約中提取餘額
  3. owner不能在未限制的情況下操作其他用戶的餘額等

9、用戶鑑權

在需要用戶鑑權的時刻,儘量使用msg.sender作爲目標方。

https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin

10、條件競爭

在智能合約的設計中,避免對交易順序的依賴,或者想辦法強制要求交易順序。

五、一些思考

在這一次整理合約編碼隱患的過程中,對智能合約本身的特殊性進行了深入瞭解。和每個語言一樣,智能合約有基於區塊鏈這個大前提在,許多代碼都出現了新的問題,如果開發者沒有注意到這些隱患,一旦出現問題,這些隱患就可能導致更大的問題發生。

截止2018年10月31日,以太坊合約審計Checklist的所以問題完成了第一輪掃描,第一輪掃描針對以太坊公開的所有合約,其中超過80%的智能合約存在1個以上的安全隱患問題。在接下來的掃描報告中,我們會公開《以太坊合約審計Checklist》並使用HaoTian對以太坊公鏈上的所有智能合約進行基於opcode的掃描分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章