作者:LoRexxar'@知道創宇404區塊鏈安全研究團隊
時間:2018年11月1日
系列文章:
- 《以太坊合約審計 CheckList 之“以太坊智能合約規范問題”影響分析報告》
- 《以太坊合約審計 CheckList 之“以太坊智能合約設計缺陷問題”影響分析報告》
- 《以太坊合約審計 CheckList 之“以太坊智能合約編碼安全問題”影響分析報告》
- 《以太坊合約審計 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、條件競爭
在智能合約中,經常容易出現對交易順序的依賴,如占山為王規則、或最后一個贏家規則。都是對交易順序有比較強的依賴的設計規則,但以太坊本身的底層規則是基于礦工利益最大法則,在一定程度的極限情況下,只要攻擊者付出足夠的代價,他就可以一定程度控制交易的順序。開發者應避免這個問題。
真實世界事件
三、漏洞影響范圍
使用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權限問題,應該遵循幾個要求:
- 合約創造后,任何人不能改變合約規則,包括規則參數大小等
- 只允許owner在合約銷毀前,從合約中提取余額
- 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的掃描分析。
智能合約審計服務
針對目前主流的以太坊應用,知道創宇提供專業權威的智能合約審計服務,規避因合約安全問題導致的財產損失,為各類以太坊應用安全保駕護航。
知道創宇404智能合約安全審計團隊: https://www.scanv.com/lca/index.html
聯系電話:(086) 136 8133 5016(沈經理,工作日:10:00-18:00)
區塊鏈行業安全解決方案
黑客通過DDoS攻擊、CC攻擊、系統漏洞、代碼漏洞、業務流程漏洞、API-Key漏洞等進行攻擊和入侵,給區塊鏈項目的管理運營團隊及用戶造成巨大的經濟損失。知道創宇十余年安全經驗,憑借多重防護+云端大數據技術,為區塊鏈應用提供專屬安全解決方案。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/732/
暫無評論