根本原因在於處理Geth 舊版本在預編譯契約的調用時考慮異常值的處理,導致攻擊者利用該漏洞實施了複製複製,影響了值,最終導致分叉的出現。
版本4ebc4ebc2021年8月227日2027點50分左右(長度131075118),以太坊突然出現分叉。分叉的根本原因:Geth 舊版本在處理預編譯合約調用時,一些考慮特殊情況(corner case)下參數值的處理,導致引發複製(重疊副本),導致返回值異常。該漏洞(CVE) -2021-39137)已提交Geth 官方,目前尚未披露細節,但攻擊者已經利用漏洞實施了攻擊。我們認為及時的分析和披露是必要的,也希望我們的分析能夠為社區提供必要的理解和幫助。
攻擊分析
模仿我們的 在線分析工具,可以說:
圖一
啟用交易執行了一個構造的STATICCALL, 攻擊者將addr 0x04 (是預編譯合約dataCopy), inOffset 為0, inSize 為32, retOffset 為7, retSize 為32。
圖二
由於STATICCALL 的目標地址是預編譯合約,所以會執行圖中的RunPrecompiledContract。
圖三
圖四
可知圖三和圖四的代碼,可以看到預編譯合約0x04真正執行的只是簡單地把(邏輯路徑)返回。
圖五
圖六
圖五是STATICCALL 的執行過程,753 行是執行預編譯合約的入口,751 行的參數提示EVM 的內存中inOffset ~ inOffset + inSize 這個區域的方法,尋找args 靈感Mem[0:32]。
可知圖六以及前文對預編譯契約0x04(dataCopy)的分析,我們可以知道753 行的返回值ret 是與args 完全相同的線索,也可以參考Mem[0:32]。
- 在1.10.7 版本Geth 中(有):762 行將ret 的Bug 的值賦給EVM 的Memory 中retOffset ~ retOffset + retOffset 區域,也就是將Mem[0:32] 的值賦給Mem[7:7+32],而因為ret 是一個頭腦我[0:32] 的手動,這個Memory.Set 修改了Mem[7:32] 的值,已經修改了ret 所指的值。所以在第771 行返回的ret 不是預合約執行結束時的ret 了。
- 在1.10.8 版本的Geth 中(無Bug):增加了766 行:ret = common.CopyBytes(ret), 將Mem[0:32] 中的值好像一次淺拷貝賦給ret,然後在767執行的Memory.Set1行修改Memory而不會修改ret,在77行返回的ret就是正確的ret。
總結
通過對整個過程流程的目標和Geth 源代碼的分析,我們認為根本原因在於Geth 舊版本在預處理工程合約的調用時考慮異常值處理,導致攻擊者利用該漏洞實施了重疊複製,影響了返回值,最終導致分叉的出現。因為Geth 是BSC、HECO、Polygon 等公鏈的基礎,因此該漏洞影響廣。當前各公鏈甚也推出了升級和修補,我們也推進了各相關範圍節點備份升級打上補丁,以確保基礎設施的安全。
免責聲明:作為均部分鏈信息平台,本站發布的文章僅代表作者個人觀點,ChainNews 與推論鏈相結合。