作者:LoRexxar'@知道創宇404區塊鏈安全研究團隊
時間:2018年8月22日
本系列上一篇:《以太坊合約審計 CheckList 之“以太坊智能合約規范問題”影響分析報告》
一、 簡介
在知道創宇404區塊鏈安全研究團隊整理輸出的《知道創宇以太坊合約審計CheckList》中,把“條件競爭問題”、“循環DoS問題”等問題統一歸類為“以太坊智能合約設計缺陷問題”。
“昊天塔(HaoTian)”是知道創宇404區塊鏈安全研究團隊獨立開發的用于監控、掃描、分析、審計區塊鏈智能合約安全自動化平臺。我們利用該平臺針對上述提到的《知道創宇以太坊合約審計CheckList》中“以太坊智能合約設計缺陷”類問題在全網公開的智能合約代碼做了掃描分析。詳見下文:
二、漏洞詳情
1、條件競爭
2016年11月29號,Mikhail Vladimirov和Dmitry Khovratovich公開了一篇《ERC20 API: An Attack Vector on Approve/TransferFrom Methods》,在文章中提到了一個在ERC20標準中存在的隱患問題,條件競爭。
這里舉一個approve函數中會出現的比較典型的例子,approve一般用于授權,比如授權別人可以取走自己的多少代幣,整個流程是這樣的:
- 用戶A授權用戶B 100代幣的額度
- 用戶A覺得100代幣的額度太高了,再次調用approve試圖把額度改為50
- 用戶B在待交易處(打包前)看到了這筆交易
- 用戶B構造一筆提取100代幣的交易,通過條件競爭將這筆交易打包到了修改額度之前,成功提取了100代幣
- 用戶B發起了第二次交易,提取50代幣,用戶B成功擁有了150代幣
想要理解上面這個條件競爭的原理,首先我們得對以太坊的打包交易邏輯有基礎認識。
https://medium.com/blockchannel/life-cycle-of-an-ethereum-transaction-e5c66bae0f6e
簡單來說就是
- 只有當交易被打包進區塊時,他才是不可更改的
- 區塊會優先打包gasprice更高的交易
所以當用戶B在待打包處看到修改的交易時,可以通過構造更高gasprice的交易來競爭,將這筆交易打包到修改交易之前,就產生了問題。
以下代碼就存在條件競爭的問題
function approve(address _spender, uint256 _value) public returns (bool success){
allowance[msg.sender][_spender] = _value;
return true
2、循環DoS問題
在以太坊代碼中,循環是一種很常見的結構,但由于以太坊智能合約的特殊性,在循環也有很多需要特別注意的點, 存在潛在的合約問題與安全隱患。
1) 循環消耗問題
在以太坊中,每一筆交易都會消耗一定的gas,而交易的復雜度越高,則該交易的gasprice越高。而在區塊鏈上,每個區塊又有最大gas消耗值限制,且在礦工最優化收益方案中,如果一個交易的gas消耗過大,就會傾向性把這個交易排除在區塊外,從而導致交易失敗。
所以,對于合約內的循環次數不宜過大,在循環中的代碼不宜過于復雜。
struct Payee {
address addr;
uint256 value;
}
Payee payees[];
uint256 nextPayeeIndex;
function payOut() {
uint256 i = nextPayeeIndex;
while (i < payees.length && msg.gas > 200000) {
payees[i].addr.send(payees[i].value);
i++;
}
nextPayeeIndex = i;
}
如果上述代碼地址列表過長,就有可能導致交易失敗。
2018年7月23日,Seebug Paper發表的《首個區塊鏈 token 的自動化薅羊毛攻擊分析》中攻擊合約就提到了這種gas優化方式。
2) 循環安全問題
在以太坊中,應該盡量避免循環次數受到用戶控制,攻擊者可能會使用過大的循環來完成Dos攻擊。
function distribute(address[] addresses) onlyOwner {
for (uint i = 0; i < addresses.length; i++) {
// transfer code
}
}
當攻擊者通過不斷添加address列表長度,來迫使該函數執行循環次數過多,導致合約無法正常維護,函數無法執行。
2016年,GovernMental合約代幣被爆出惡意攻擊,導致地址列表過長無法執行,超過1100 ETH被困在了合約中。
三、漏洞影響范圍
使用Haotian平臺智能合約審計功能可以準確掃描到該類型問題。

基于Haotian平臺智能合約審計功能規則,我們對全網的公開的共39548 個合約代碼進行了掃描,其中共24791個合約涉及到這類問題。
1、 條件競爭
截止2018年8月10日為止,我們發現了22981個存在approve條件競爭的合約代碼,其中15325個合約仍處于交易狀態,其中交易量最高的10個合約情況如下:

2、 循環DoS問題
截止2018年8月10日為止,我們發現了1810個存在潛在循環dos問題的合約代碼,其中1740個合約仍處于交易狀態,其中交易量最高的10個合約情況如下:

四、修復方式
1)條件競爭
關于這個問題的修復方式討論很多,由于這屬于底層特性的問題,所以很難在智能合約層面做解決,在代碼層面,我們建議在approve函數中加入
require((_value == 0) || (allowance[msg.sender][_spender] == 0));
將這個條件加入,在每次修改權限時,將額度修改為0,再將額度改為對應值。
在這種情況下,合約管理者可以通過日志或其他手段來判斷是否有條件競爭發生,從風控的角度警醒合約管理者注意該問題的發生。范例代碼如下:
function approve(address _spender, uint256 _value) isRunning validAddress returns (bool success) {
require(_value == 0 || allowance[msg.sender][_spender] == 0);
allowance[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
2)循環DoS問題
在面臨循環DoS問題產生的場景中,最為常見的就是向多個用戶轉賬這個功能。
這里推薦代碼中盡量避免用戶可以控制循環深度,如果無法避免的話,盡量使用類似withdrawFunds這種函數,循環中只分發用戶提幣的權限,讓用戶來提取屬于自己的代幣,通過這種操作可以大幅度節省花費的gas開支,也可以一定程度避免可能導致的問題。代碼如下所示:
function distribute(address[] addresses) onlyOwner {
for (uint i = 0; i < addresses.length; i++) {
if (address_claimed_tokens[addresses[i]] == 0) {
balances[owner] -= transferAmount;
balances[addresses[i]] += transferAmount;
address_claimed_tokens[addresses[i]] += transferAmount;
Transfer(owner, addresses[i], transferAmount);
}
}
}
五、一些思考
在分析了許多智能合約已有的漏洞以及合約以后,我發現有一類問題比較特殊,這些問題的誕生根本原因都是因為以太坊智能合約本身的設計缺陷,再加上開發者對此沒有清晰的認識,導致了合約本身的一些隱患。
文章中提到的條件競爭是個比較特殊的問題,這里的條件競爭涉及到了智能合約底層實現邏輯,本身打包邏輯存在條件競爭,我們無法在代碼層面避免這個問題,但對于開發者來說,比起無緣無故的因為該問題丟失代幣來說,更重要的是合約管理者可以監控到每一筆交易的結果,所以我們加入置0的操作來提醒合約管理者、代幣持有者該問題,盡量避免這樣的操作發生。
而循環Dos問題就是一個針對開發者的問題,每一次操作就是一次交易,每次交易就要花費gas,交易越復雜花費的gas越多,而在區塊鏈上,每個區塊又有最大gas消耗值限制,且在礦工最優化收益方案中,如果一個交易的gas消耗過大,就會傾向性把這個交易排除在區塊外,從而導致交易失敗。這也就直接導致了在交易中,我們需要盡可能的優化gas花費,避免交易失敗。
我們在對全網公開的合約代碼進行掃描和監控時容易發現,有很大一批開發人員并沒有注意到這些問題,其中條件競爭問題甚至影響廣泛,有超過一半以上的公開代碼都受到影響。
這里我們建議所有的開發者重新審視自己的合約代碼,檢查是否存在設計缺陷問題,避免不必要的麻煩以及安全問題。
智能合約審計服務
針對目前主流的以太坊應用,知道創宇提供專業權威的智能合約審計服務,規避因合約安全問題導致的財產損失,為各類以太坊應用安全保駕護航。
知道創宇404智能合約安全審計團隊: https://www.scanv.com/lca/index.html
聯系電話:(086) 136 8133 5016(沈經理,工作日:10:00-18:00)
區塊鏈行業安全解決方案
黑客通過DDoS攻擊、CC攻擊、系統漏洞、代碼漏洞、業務流程漏洞、API-Key漏洞等進行攻擊和入侵,給區塊鏈項目的管理運營團隊及用戶造成巨大的經濟損失。知道創宇十余年安全經驗,憑借多重防護+云端大數據技術,為區塊鏈應用提供專屬安全解決方案。
六、REF
-
ERC20 API: An Attack Vector on Approve/TransferFrom Methods https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit
-
Life Cycle of an Ethereum Transaction https://medium.com/blockchannel/life-cycle-of-an-ethereum-transaction-e5c66bae0f6e
-
首個區塊鏈 token 的自動化薅羊毛攻擊分析 http://www.bjnorthway.com/646/
-
GovernMental's 1100 ETH : https://www.reddit.com/r/ethereum/comments/4ghzhv/governmentals_1100_eth_jackpot_payout_is_stuck/
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/679/
暫無評論