EVM(Ethereum Virtual Machine)互換ブロックチェーンは、Ethereumブロックチェーンのスマートコントラクト機能、プログラミング言語(Solidity)、およびツールエコシステムとの互換性を持つように設計されています。このプロセスにおいて、EVM準拠の仮想マシンの実装は重要なステップの一つです。しかし、調査を進める中で、異なる実装においてEVMの互換性を維持することは容易ではないことがわかりました。
この問題を解決するために、BlockSecはEVM実装内のバグおよびセキュリティ脆弱性を体系的に特定できる内部システムを開発しました。このシステムは効果的であることが証明され、Aurora Engineで4つのバグ、Moonbeamで4つのバグが発見されました。これらはすべて報告・修正済みです。なお、このテスト手法は昨年、Solana rbpf実装における2つの重大な脆弱性(CVE-2021–46102、CVE-2022–23066)の特定にも使用されました。
1. 背景
近年、低ガス料金、高スループット、高パフォーマンスを最適化した多様なブロックチェーンが提案されています。それに伴い、新しい仮想マシンや開発言語も提案されており、既存のSolidity開発者にとって移行のハードルが高まっています。このような状況の中、ユーザーがSolidityで開発したDAppsをこれらの新しいチェーンにデプロイできるEVM互換ソリューションが多数提案されています。AuroraとMoonbeamはそのようなEVM互換ソリューションの代表例です。しかし、これらのEVM実装の堅牢性、信頼性、および精度は不明であり、注意が必要です。そのため、差分ファジング技術を活用して、実装に欠陥がないかを検証しました。
2. 差分ファジング
基本的なアイデアは、これらのEVM実装(Aurora、Moonbeamなど)と最新のEthereumクライアント(geth)に同一の入力を与え、同じ出力が得られるかを確認することです。具体的には、Ethereum上の過去のトランザクションを収集し、さまざまな戦略でコントラクトコードとトランザクション状態を変異させてテストケースを生成しました。調査の結果、Aurora EngineとMoonbeamが一部のケースで仕様に準拠していないことが判明しました。幸いなことに、報告されたすべての問題は対処済みであり、以下にその詳細を共有します。
3. 発見されたバグ
これらのバグの多くはプリコンパイルコントラクトに存在し、根本原因と影響はさまざまです。例えば、ノンス値の計算に影響するバグや、ガス計算に影響してDoS攻撃を引き起こす可能性のあるバグも存在します。発見されたバグはすべてEVM仕様の指定された実行ロジックに違反しており、特定のケースで予期しない動作を引き起こす可能性があります。各バグの詳細な説明は以下をご参照ください。
3.1 不適切なバリデーション
暗号ロジックは複雑です。EVMバイトコードで対応するロジックを実装すると、ガス使用量が非常に大きくなる可能性があります。一方、ネイティブコードで開発されたプリコンパイルコントラクトはパフォーマンスを向上させることができます。しかし、不適切なバリデーションにより、プリコンパイルコントラクトにいくつかのバグが発見されました。
Aurora Engine: ecPairing
このバグはプリコンパイルコントラクト ecPairing に発見されました。
ecPairing の入力は2つの楕円曲線上の複数の点で構成されます。仕様によると、点 (0, 0) は両方の曲線上に存在し、有効な入力であるべきです:


しかし、Aurora Engine(バージョン2.7.0)は入力に点 (0,0) が含まれる場合にリバートします。

関連するPRはこちらです。
Moonbeam: ecMul
このバグはプリコンパイルコントラクト ecMul にあります。Aurora Engineとは異なり、Moonbeamは有効な入力に対してトランザクションをリバートします。仕様によると、プリコンパイルコントラクト ecMul は入力の長さが64未満の場合、ゼロでパディングする必要があります。しかし、Moonbeamはパディング操作を行う代わりにリバートします。

また、プリコンパイルコントラクト ecAdd と modexp にも同様の問題があることが確認されました。
Moonbeam: ecRecover
このバグはEthereumアドレスの復元に使用されるプリコンパイルコントラクト ecRecover にあります。
仕様によると、input[32..63] の v はU256識別子を表し、27または28のいずれかであることが期待されます。それ以外の場合、ecRecover は何も返さないべきです(ただし、トランザクション全体はリバートされるべきではありません)。
しかし、Moonbeamは以下の2つの誤りを犯しています:
- input[32..63] をU256型に変換してから値を確認する代わりに、input[63] のみを確認している。
- 識別子が0または1の場合に入力を有効とみなしている(27または28のみが有効であるべき)。

Moonbeam: ecPairing
このバグはプリコンパイルコントラクト ecPairing にあります。Moonbeamは入力が無効な場合でもトランザクションをリバートしません。仕様によると、プリコンパイルコントラクト ecPairing への入力は192の倍数であるべきです。そうでない場合、トランザクションはリバートされるべきです。

しかし、Moonbeamは上記の要件が満たされない場合でもトランザクションをリバートしません。
3.2 不正なガス計算
各プリコンパイルコントラクトにはガス使用量を決定するアルゴリズムがあります。不正なガス計算はDoS攻撃を引き起こす可能性があります。
Aurora EngineとMoonbeamの両方でガス計算が不正である2つのバグが発見されました。
Aurora Engine: modexp
このバグはプリコンパイルコントラクト modexp にあります。ガス使用量の計算アルゴリズムはEIP-2565で定義されています。ガス使用量はイテレーション数に関連しています。
イテレーション数を計算するアルゴリズムは以下の通りです。
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は max(iteration_count, 1) の代わりに iteration_count をそのまま返します。この場合、返される値(iteration_count)が0になる可能性があり、特定のケースでAuroraが期待より少ないガス料金を請求することになります。
関連するイシューのリンクはこちらです。
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の実装はこのアルゴリズムに従っています。
しかし、仕様によると、ガス計算式は exponent の最上位32バイトを使用する必要があります:

3.3 ノンスインクリメントのバグ
外部所有アカウント(EOA)のノンスは、そのアドレスによって署名された成功したトランザクションの数を示します。しかし、Aurora Engineでは無効なトランザクションを送信することでノンスが増加してしまうことが判明しました。
EIP-1559によると、トランザクションを実行する前に、EVMは署名者が転送するネイティブトークン(ETHなど)と必要なガスをカバーするのに十分な残高を持っていることを確認する必要があります。そうでない場合、トランザクションはドロップされ、署名者のノンスは増加されるべきではありません。

Aurora Engineはこの状況でトランザクションをドロップしますが、それでも署名者のノンスを増加させてしまいます。
関連するPRはこちらです。
3.4 不正なオペコード実装
このバグは特定のオペコード(PUSH)の実装に関するものです。PUSHオペコードに続くバイトが不完全な場合、それらは右詰めにされるべきです。例えば、バイトコード 0x64ffff は次のようにデコードされます:
PUSH5 0xffff
オペランドは右詰めにされるべきであるため、値 0xffff000000 がスタックにプッシュされるべきです。しかし、Aurora EngineとMoonbeamの両方のEVM実装では、代わりに 0xffff をプッシュしており、これは不正です。
関連するPRはこちらです。
4. 私たちのサービス
BlockSecでは、異なるブロックチェーン実装におけるEVM互換性とセキュリティを維持することの重要性を理解しています。そのため、EVM実装の脆弱性を容易に特定できる体系的なバグ検出アプローチを開発しました。
私たちの内部システムは、EVM実装におけるバグおよびセキュリティ脆弱性の特定において高い効果を発揮することが証明されています。Aurora Engineで4つのバグ、Moonbeamで4つのバグを報告・修正することに成功しました。さらに、私たちのテスト手法は昨年、Solana rbpf実装における2つの重大な脆弱性(CVE-2021–46102、CVE-2022–23066)の特定にも使用され、このアプローチの有効性を示しました。
私たちの体系的なバグ検出アプローチを採用することで、クライアントはEVM実装が安全で信頼性の高いものであると確信できます。私たちはEVM互換性とセキュリティに関する最高水準のソリューションを提供し、クライアントがユーザーやステークホルダーとの信頼関係を構築する支援をすることに取り組んでいます。
BlockSecについて
BlockSecチームはブロックチェーンエコシステムのセキュリティに特化し、主要なDeFiプロジェクトと協力してその製品を保護しています。チームは学術界と産業界の両方から優秀なセキュリティ研究者および経験豊富な専門家によって設立されました。彼らは権威ある会議で複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのゼロデイ攻撃を複数報告し、影響力の大きいセキュリティインシデントの詳細な分析レポートを公開しています。



