Security
導致 DAO hack 的遞歸是如何創建的?
我理解,如果 DAO 合約具有向合約 X 發送資金的提款功能 - 合約 X 可能是惡意的,並使用回退功能再次呼叫提款功能。然而 - 在 DAO 合約的情況下,X 是 DAO 的另一個副本,因此它沒有這種惡意行為。
那麼遞歸發送究竟是如何實現的呢?
這是
withdrawRewardFor
函式中有問題的程式碼行:if (!rewardAccount.payOut(_account, reward)) <-- reentrant exploit throw; paidOut[_account] += reward;
payOut 將呼叫接收
payout
函式:function payOut(address _recipient, uint _amount) returns (bool) { .. if (_recipient.call.value(_amount)()) { .. }
如果
_recipient
是合約,這將呼叫合約的回退函式。該接收者反過來可以再次呼叫 TheDao 合約。由於withdrawRewardFor
沒有防止重入,它再次被允許。這意味著在程式碼被允許繼續使用之前持續支付paidOut[_account] += reward;
。更多的乙太坊攻擊:Race-To-Empty 是真正的交易,這是在攻擊前一周寫的,對這種類型的黑客攻擊進行了更詳細的分析,並提出了解決方案。
要回答您的問題,您需要區分契約和提案。
DAO 是一種合約,它編纂了通過****提案提供資金的規則。
提案可以是投資請求,也可以是提取您自己的資金的請求(這稱為拆分提案)。當拆分提案被批准後,它會呼叫 splitDAO API 創建一個子 DAO(子合約)。這個子合約的程式碼確實和原來的 DAO 是一樣的。
splitDAO 函式將請求的資金轉移到子 DAO。但它也呼叫了提案提供的預設函式,表面上是為了處理正在轉移的資金。 而這個函式是在資金劃轉操作之後,資金從DAO的總餘額中扣除之前,使用者餘額設置為零之前呼叫的。
遞歸漏洞利用嵌入在這個預設函式中。它再次簡單地呼叫了 splitDAO。
實際上,資金轉移、總餘額減少和使用者餘額歸零應該是原子操作。還可以實施其他方法(鎖、互斥鎖)以確保在執行任何外部使用者提供的程式碼之前完成整個拆分操作。