導致 EIP 150 硬分叉的狀態膨脹攻擊是如何執行的?
我了解
SELFDESTRUCT
操作碼的錯誤定價允許以便宜的方式創建許多空賬戶,如為什麼允許空賬戶在區塊鏈上? 該問題的答案還包括創建許多空帳戶的交易。該交易/攻擊是如何工作的?當合約呼叫SELFDESTRUCT
時,它不能SELFDESTRUCT
再次……或者可以嗎?
古代歷史,但這裡有:-)
最初的攻擊有一個協調契約。後來 的版本有一個更複雜的算法來生成空賬戶地址,大概是為了阻止任何未來的清理工作。這只是通過地址空間增量工作。
每次呼叫協調合約時——它被呼叫了 4750 次——它執行以下操作:
(1) 創建子契約。
0000 PUSH32 0x6004600c60003960046000f3600035ff00000000000000000000000000000000 0021 PUSH1 0x00 0023 MSTORE 0024 PUSH1 0x20 0026 PUSH1 0x00 0028 PUSH1 0x00 002a CREATE
這會在新地址上創建以下子合約的單個副本:
;; Child contract (in full) PUSH1 0x00 CALLDATALOAD SELFDESTRUCT
此子合約的程式碼嵌入在
PUSH32
協調合約的第一個中:0000 PUSH32 0x6004600c60003960046000f3600035ff00000000000000000000000000000000
this 的前 12 個字節是子合約的建構子的程式碼;接下來的四個字節是子契約程式碼本身,如上所述。
(2) 從儲存中載入一個計數器:
002b PUSH1 0x00 002d SLOAD 002e DUP1
這會跟踪正在創建的空/膨脹帳戶,並允許對協調合約的後續呼叫從前一個中斷的地方繼續。
(3) 循環遍歷合約的其餘部分:每次循環迭代呼叫子合約40次(即以下程式碼重複40次)
0030 PUSH1 0x01 0032 ADD 0033 DUP1 0034 PUSH1 0x00 0036 MSTORE 0037 PUSH1 0x00 0039 DUP1 003a PUSH1 0x20 003c DUP2 003d DUP1 003e DUP8 003f PUSH1 0x06 0041 CALL 0042 POP
在這裡,它向計數器添加一個,複製它,將一個副本放回記憶體中,然後將計數器的另一個副本作為呼叫數據呼叫子合約。
子合約將其發送的數據(計數器)解釋為地址,並在發送時
SELFDESTRUCT
將其(零)值發送到該地址,從而創建一個空膨脹帳戶。這裡的關鍵點是子合約直到目前交易執行結束才真正自毀,因此它可以在一筆交易中被多次呼叫。黃皮書裡的描述是“暫停執行並註冊賬號以便以後刪除”(強調我的);它將在目前事務終止時被刪除。僅在每次呼叫協調合約時重新創建子合約就足夠了;然後可以在其中多次呼叫它。
(4) 檢查剩餘gas,如果有足夠的則返回並再次循環所有40個呼叫,繼續增加地址計數器。
0328 GAS 0329 PUSH2 0x6000 032c LT 032d PUSH3 0x00002f 0331 JUMPI
因此,對協調合約的每次呼叫都能夠創建數千個膨脹賬戶,具體取決於最初供應的天然氣量。
(5) 最後,儲存地址計數器,為下一次呼叫做好準備。
0332 PUSH1 0x00 0334 SSTORE
當然,問題在於,通過 SELFDESTRUCT 創建帳戶並沒有使用應有的燃料。這在EIP150:長期天然氣成本變化中得到了解決。