Solidity

Ethernaut 20 級拒絕可能不再可解決。為什麼?

  • March 16, 2022

我解決了除 20 級拒絕之外的所有級別的Ethernaut遊戲:https ://ethernaut.openzeppelin.com/level/0xcE1BB92eeb71AF5Fec09D38B0c854d55285f6e04

最終還是放棄了,在作者的部落格上查找了解決方法:https ://github.com/sigp/solidity-security-blog#dos-vuln

我使用 Remix 在 Rinkeby 測試網路上部署了以下合約:

pragma solidity ^0.8.7;

contract DenialAttack {
   fallback() external payable {
       assert(false);
   }
}

該聯繫人的地址是:0x828F2073947a6d3De9caeEf48c7eCa989436a14B

然後我在 Ethernaut 上創建了一個新的 Denial 實例。接下來我打電話(來自開發工具):

contract.setWithdrawPartner("0x828F2073947a6d3De9caeEf48c7eCa989436a14B")

我確保partner已成功將其設置為我的攻擊契約。我送出了我的實例,但它告訴我我還沒有完成關卡。然後我執行(在我的 Brave 瀏覽器的開發工具中):

contract.withdraw()

並且交易成功完成。是它在 Etherscan 上的外觀。

預期的行為是我在送出實例後完成關卡並呼叫withdraw失敗並出現氣體不足錯誤。

好的,所以這應該足以重現該問題。

這裡的想法是將所有(或大部分?) gaspartner.call.value(amountToSend)("");傳遞給被呼叫者合約(在我的情況下是傳遞給的執行。因此,我們實現了拒絕服務。DenialAttack``Denial``withdraw()

然而,似乎實際發生的事情是call沒有將所有的氣體傳遞給被呼叫者合約,並且留下了足夠的氣體來完成withdraw().

我還嘗試了其他方法來燃燒所有氣體,例如在循環中多次迭代或執行類似於此處描述的操作。沒有任何效果。

我的假設是,這個解決方案曾經有效,但乙太坊中發生了一些變化,在call沒有指定氣體限制的情況下使用氣體時如何通過氣體,並且這個水平不再可解決。也許https://github.com/ethereum/EIPs/issues/114是相關的?

我的問題是:我是對的嗎?這個水平還可以解決嗎?如果不是,為什麼?究竟發生了什麼變化?什麼時候?

謝謝!

多虧了賞金,我才看到了這個問題,並且非常好奇為什麼它不可能……我離 20 級並不遠,所以我跳了幾步嘗試一下。

這個問題似乎只存在於solidity > 0.8.5(使用松露測試),我測試了0.6.0到0.8.5(排除)範圍內的幾個版本,行為完全正常。

有了一個簡單的契約:

pragma solidity ^0.8.0;

contract TargetCall {
   
   fallback() external payable {
       assert(false);
   }
}

我看到了和你一樣的行為,它是呼叫內部的恢復而不是斷言,所以執行可以繼續……

查看字節碼,我看到了一些奇怪的東西:

0x60806040526000601057600f6012565b5b005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fdfea264697066735822122022ef0b693779530b61308a11d0037e0882593e0cda55eb30d234198773a38f6464736f6c634300080a0033

斷言位於:fdfe但在 OPCODE 視圖中是:

  • 0xFD:恢復
  • 0xFE:無效(斷言錯誤)

我認為這assert(false)是“優化”到一個簡單的revert()但無論我放入什麼條件(即使是一些不應該優化的..)它都會產生類似的字節碼(也許我錯過了一些東西)。如果真的是優化,為什麼會有 0xFE 無效操作碼呢?

我的解決方案是強制使用無效的 OPCODE 而不是revert()

因此,修改fdfefefewich 轉換為:

  • 0xFE 無效(斷言錯誤)
  • 0xFE 無效(斷言錯誤)

部署這個字節碼解決了這個挑戰。

該合約目前部署在 Rinkeby 網路上:0x2C68B51837c38B5f9B1e20acf607efed175B9BC7

這個水平還可以解決嗎?

是的 !

究竟發生了什麼變化?

我想關於 assert() 的solidity >0.8.5 行為發生了變化,但我找不到具體資訊讓我想出一個不那麼骯髒的解決方案。如果有人有適當的解釋/連結來更改摘要,我也會很好奇!

PS:如果我發現任何東西,我將繼續尋找解釋該行為的更改日誌並相應地編輯我的答案。

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