以太坊突遭分叉的罪魁禍首:CVE-2021-39137 漏洞成因及攻擊分析

買賣虛擬貨幣

北京時間2021年8月27日20點50分左右(區塊高度13107518),以太坊突然出現分叉。我們透過分析Geth的程式碼版本修改和這筆造成分叉的交易(

0x1cb6fb36633d270edefc04d048145b4298e67b8aa82a9e5ec4aa1435dd770ce4

)釐清了以太坊分叉的根本原因:Geth舊版本在處理預編譯合約呼叫時,並未考慮特殊情況(corner case)下引數值的處理,從而引發重疊複製(overlapping copy),導致返回值異常。該漏洞(CVE-2021-39137)已提交Geth官方,目前尚未披露細節,但攻擊者已經利用漏洞實施了攻擊。我們認為及時的分析和披露是必要的,也希望我們的分析能夠為社羣提供必要的理解和幫助。

攻擊分析

運用我們的線上分析工具

https://tx.blocksecteam.com/tx/0x1cb6fb36633d270edefc04d048145b4298e67b8aa82a9e5ec4aa1435dd770ce4

,可以看出:

圖一

這筆交易執行了一個精心構造的 STATICCALL, 攻擊者將 addr 設為0x04(是預編譯合約dataCopy), inOffset 為 0, inSize 為 32, retOffset 為 7, retSize 為 32。

圖二

由於STATICCALL的目標地址是預編譯合約,所以會執行圖二中的RunPrecompiledContract。

圖三

圖四

根據圖三和圖四的程式碼,可以看到預編譯合約0x04真正執行的邏輯只是簡單地把 in (指標)返回。

圖五

圖六

圖五是STATICCALL的執行過程,753 行是執行預編譯合約的入口,751 行的 args 指向 EVM 的 Memory 中 inOffset ~ inOffset + inSize 這篇區域的指標,也就是說args 指向 Mem[0:32];

根據圖六以及前文對預編譯合約0x04(dataCopy)的分析,我們可以知道 753 行的返回值 ret 是與 args 完全相同的指標,也指向 Mem[0:32];

在 1.10.7 版本的 Geth 中(有Bug): 762 行將 ret 指向的值賦給 EVM 的 Memory 中 retOffset ~ retOffset + retOffset 這篇區域, 也就是將 Mem[0:32] 的值賦給 Mem[7:7+32]; 而由於 ret 是一個指向Mem[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.Set 只會修改 Memory 而不會修改 ret, 在 771 行返回的 ret 就是正確的 ret。

總結

透過對整個攻擊流程的梳理和Geth原始碼的分析,我們認為根本原因在於Geth舊版本在處理預編譯合約的呼叫時並未考慮異常值的處理,導致攻擊者利用該漏洞實施了重疊複製,影響了返回值,最終導致分叉的出現。由於Geth是BSC、HECO、Polygon等公鏈的基礎,因此該漏洞影響範圍甚廣。目前各公鏈也先後推出了升級和補丁,我們也呼籲各相關節點儘早升級打上補丁,以確保基礎設施的安全。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读