Solidity

向回退函式轉發gas的意義及其與重入攻擊的關係

  • February 19, 2022

我正在閱讀https://solidity-by-example.org/fallback/以及<address>.send 與 <address>.transfer 最佳實踐用法中發送、轉移和呼叫之間的區別?.

根據我目前的理解,顯然由於預設情況下 send 和 transfer 對前向回退函式的 gas 量施加了限制(我不知道這是什麼意思),它本質上會阻止重入)。

由於 call() 轉發所有氣體(同樣,我不確定這是什麼意思)。為了防止重入,我們需要在呼叫 call() 之前更改所有狀態。

有人可以向我解釋轉發 gas 是什麼意思(我的理解是你為每筆交易設置了 gas 限制,如果交易沒有用完所有的 gas,那麼剩餘的 gas 將返回給 msg 發送者)以及如何它與重入攻擊有關嗎?

此外,我嘗試了https://solidity-by-example.org/fallback/中的範常式式碼。有人可以解釋為什麼即使我將氣體限制設置為 3000000,輸出日誌顯示 80000000 氣體?

首先,讓我們創建一個基本的重入漏洞合約:

contract Safe {
  mapping(address =&gt; uint) public userBalances;
  
  function deposit() public payable {
      userBalances[msg.sender] += msg.value;
  }

  function withdraw(uint _amount) public {
      require(userBalances[msg.sender] &gt;= _amount, "low balance");
      payable(msg.sender).call({value: _amount});
      userBalances[msg.sender] -= _amount;
  }
}

如您所見,我們沒有設置call可以使用的最大氣體。這是什麼意思?您可以限制外部函式呼叫可以使用的氣體量。看看這裡。如果你沒有設置這個怎麼辦?它可以使用您的所有氣體(近 1m 氣體)。不好嗎?有可能。備份函式用於合約在收到時應該做什麼ether(不僅如此,但現在到現在已經足夠了)。

我們是一個受歡迎的 DeFi 項目。因此,我們的合約中將有數千個乙太幣。

這個攻擊者創建了一個攻擊我們的安全合約的合約。而這個合約有這個功能:

function attack() public onlyOwner {
  Safe.deposit({value: 1 ether});   
  Safe.withdraw(1 ether);
}

這個功能基本上是為攻擊者的智能合約充值和提現 1 個乙太幣。而且我們攻擊者的智能合約也有一個回退功能:

fallback() external payable {
  Safe.withdraw(1 ether);
}

好的,讓我們深吸一口氣,一步一步看我們在做什麼:

  1. 創建攻擊者合約
  2. 呼叫攻擊功能 - 這意味著存入和提取 1 個乙太幣。
  3. 當我們嘗試退出時,Safe 合約會嘗試呼叫我們的回退函式。並且該函式也嘗試呼叫withdraw函式。

你能看到嗎?有一個循環。這個循環將一直持續到所有安全合約資金結束。因為會有錯誤。之後,我們將收到所有資金。因為我們的資金沒有減少(因為我們的循環)。

但是,如果您將呼叫函式行更改為:

payable(msg.sender).call({value: _amount, gas: 30000});

可能會沒問題(取決於氣體計算)。但最好的解決方案是先減少 userBalances,然後再發送他的錢。

引用自:https://ethereum.stackexchange.com/questions/122010