EVM(以太坊虛擬機)相容區塊鏈旨在與以太坊區塊鏈的智能合約功能、程式語言(Solidity)及工具生態系統保持相容。在此過程中,實作符合 EVM 規範的虛擬機是關鍵步驟之一。然而,在我們的研究中發現,在不同實作中維持 EVM 相容性並非易事。
為解決此問題,BlockSec 開發了一套內部系統,能夠系統性地定位 EVM 實作中的漏洞與安全弱點。該系統被證明行之有效,分別在 Aurora Engine 中發現了四個漏洞,在 Moonbeam 中也定位了四個漏洞,且所有漏洞均已回報並修復。值得注意的是,這套測試方法去年也被用於定位 Solana rbpf 實作中的兩個重大漏洞(CVE-2021–46102、CVE-2022–23066)。
1. 背景
如今,許多不同的區塊鏈以低 Gas 費、高吞吐量及優越效能為優化目標相繼被提出。在此情形下,新型虛擬機或開發語言應運而生,這提高了原本 Solidity 開發者轉型的門檻。因此,許多 EVM 相容方案被提出,允許使用者將以 Solidity 開發的 DApp 部署至這些新鏈上。Aurora 與 Moonbeam 便是這類 EVM 相容方案的代表。然而,這些 EVM 實作的健壯性、可靠性與精確性仍屬未知,值得我們關注。為此,我們採用差分模糊測試技術,檢驗這些實作是否存在缺陷。
2. 差分模糊測試
其核心思想是將相同的輸入分別提供給這些 EVM 實作(例如 Aurora、Moonbeam)以及最先進的以太坊客戶端(即 geth),以檢查它們是否產生相同的輸出。具體而言,我們收集以太坊上的歷史交易,並以不同策略對合約程式碼與交易狀態進行變異,以生成測試案例。研究結果顯示,Aurora Engine 與 Moonbeam 在某些情況下未能符合規範。所幸,所有回報的問題均已獲得處理,以下我們將分享詳細內容。
3. 發現的漏洞
這些漏洞大多位於預編譯合約中,其根本原因與影響各有不同。例如,部分漏洞可能影響 nonce 值的計算,而其他漏洞則可能影響 Gas 計算,進而導致 DoS 攻擊。所有發現的漏洞均違背了 EVM 規範所規定的執行邏輯,並可能在特定情況下導致非預期的行為。請參閱下方各漏洞的詳細描述。
3.1 驗證不當
密碼學邏輯較為複雜。在此情形下,以 EVM 位元組碼實作對應邏輯可能導致相當高的 Gas 消耗。相較之下,以原生程式碼開發的預編譯合約能提升效能。然而,我們在預編譯合約中發現了數個因驗證不當所引發的漏洞。
Aurora Engine:ecPairing
此漏洞存在於預編譯合約 ecPairing 中。
ecPairing 的輸入由兩條橢圓曲線上的多個點組成。根據規範,點 (0, 0) 在兩條曲線上均存在,應為有效輸入:


然而,Aurora Engine(版本 2.7.0)在輸入中包含點 (0,0) 時將執行回退(revert)。

相關 PR 請見此處。
Moonbeam:ecMul
此漏洞存在於預編譯合約 ecMul 中。與 Aurora Engine 不同,Moonbeam 在輸入有效時卻回退了交易。根據規範,當輸入長度小於 64 時,預編譯合約 ecMul 應以零填充輸入。然而,Moonbeam 並未執行填充操作,而是直接回退。

我們也發現預編譯合約 ecAdd 與 modexp 存在相同問題。
Moonbeam:ecRecover
此漏洞存在於預編譯合約 ecRecover 中,該合約用於還原以太坊地址。
根據規範,input[32..63] 中的 v 代表一個 U256 識別碼,預期值應為 27 或 28,否則 ecRecover 不應返回任何內容(但整個交易不應回退)。
然而,Moonbeam 在此犯了兩個錯誤:
- 它僅檢查 input[63],而非將 input[32..63] 轉換為 U256 類型後再檢查其值。
- 當識別碼為 0 或 1 時,輸入被視為有效(正確應為 27 或 28)。

Moonbeam:ecPairing
此漏洞存在於預編譯合約 ecPairing 中。Moonbeam 在輸入無效時並未回退交易。根據規範,預編譯合約 ecPairing 的輸入應為 192 的倍數,否則交易應回退。

然而,Moonbeam 在不符合上述要求時並未回退交易。
3.2 Gas 計算錯誤
每個預編譯合約均有一套演算法來決定 Gas 使用量。錯誤的 Gas 計算可能導致 DoS 攻擊。
我們在 Aurora Engine 與 Moonbeam 中各發現了兩個 Gas 計算錯誤的漏洞。
Aurora Engine:modexp
此漏洞存在於預編譯合約 modexp 中。計算 Gas 使用量的演算法由 EIP-2565 定義,Gas 使用量與迭代次數相關。
計算迭代次數的演算法如下:
def calculate_iteration_count(exponent_length, exponent):
iteration_count = 0
if exponent_length <= 32 and exponent == 0: iteration_count = 0
elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1
elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1)
return max(iteration_count, 1)
根據上述演算法,迭代次數至少為 1。然而,Aurora Engine 直接返回 iteration_count,而非 max(iteration_count, 1)。在此情況下,返回值(即 iteration_count)可能為 0,這意味著 Aurora 在特定情況下收取的 Gas 費用將低於預期。
相關 issue 連結請見此處。
Moonbeam:modexp
此漏洞同樣存在於預編譯合約 modexp 的 calculate_iteration_count 函式中,但發現於 Moonbeam。
當 exponent_length 大於 32 時,iteration_count 以下列演算法計算:
(8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1)
注意此處使用 exponent & (2**256 - 1),取的是 exponent 的最低 32 個位元組,Moonbeam 的實作遵循了此演算法。
然而,根據規範,Gas 計算公式應使用 exponent 的最高 32 個位元組:

3.3 Nonce 遞增漏洞
外部擁有帳戶(EOA)的 nonce 代表該地址成功簽署的交易數量。然而,我們注意到在 Aurora Engine 上,透過發送無效交易也可使 nonce 增加。
根據 EIP-1559,在執行交易之前,EVM 應確保簽署者擁有足夠的餘額以支付轉移的原生代幣(例如 ETH)及所需的 Gas。否則,交易應被丟棄,且簽署者的 nonce 不應增加。

儘管 Aurora Engine 在此情況下丟棄了交易,但仍增加了簽署者的 nonce。
相關 PR 請見此處。
3.4 操作碼實作錯誤
此漏洞涉及特定操作碼(即 PUSH)的實作。當緊隨 PUSH 操作碼的位元組不完整時,應進行右對齊處理。例如,位元組碼 0x64ffff 可被解碼為:
PUSH5 0xffff
由於運算元應右對齊,應將值 0xffff000000 推入堆疊。然而,Aurora Engine 與 Moonbeam 的 EVM 實作均推入了 0xffff,此為錯誤行為。
相關 PR 請見此處。
4. 我們的服務
在 BlockSec,我們深知在不同區塊鏈實作中維持 EVM 相容性與安全性的重要性。因此,我們開發了一套系統性的漏洞偵測方法,能夠輕鬆定位 EVM 實作中的弱點。
我們的內部系統在定位 EVM 實作中的漏洞與安全弱點方面已被證明極為有效,成功回報並修復了 Aurora Engine 中的四個漏洞及 Moonbeam 中的四個漏洞。此外,我們的測試方法去年也被用於定位 Solana rbpf 實作中的兩個重大漏洞(CVE-2021–46102、CVE-2022–23066),充分彰顯了我們方法的有效性。
透過實施我們系統性的漏洞偵測方法,客戶可以確信其 EVM 實作是安全可靠的。我們致力於提供頂尖的 EVM 相容性與安全性解決方案,協助客戶建立與使用者及利益相關者之間的信任。
關於 BlockSec
BlockSec 團隊專注於區塊鏈生態系統的安全,並與領先的 DeFi 專案合作以保障其產品安全。團隊由來自學術界與業界的頂尖安全研究人員及資深專家創立,曾在知名學術會議發表多篇區塊鏈安全論文,回報了數起 DeFi 應用的零日攻擊,並發布了多起重大安全事件的詳細分析報告。



