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開発者にとっての変革のハードルが高くなります。そこで、多くのEVM互換ソリューションが提案されており、これによりユーザーはこれらの新しいチェーン上でSolidityで開発されたDAppsを展開できます。AuroraとMoonbeamは、これらのEVM互換ソリューションの代表格です。しかし、これらのEVM実装の堅牢性、信頼性、および精度は不明であり、注意に値します。この目的のために、私たちは差分ファジング技術を利用し、実装に欠陥がないか確認します。
2. 差分ファジング
根本的な考え方は、これらのEVM実装(例: Aurora、Moonbeam)と最先端のEthereumクライアント(すなわち、geth)に同一の入力を与え、それらが同じ出力を生成するかどうかを確認することです。具体的には、Ethereum上の履歴トランザクションを収集し、コントラクトコードとトランザクション状態をさまざまな戦略で変更してテストケースを生成します。私たちの調査結果は、Aurora EngineとMoonbeamが一部のケースで仕様に準拠していないことを示しています。幸いなことに、報告されたすべての問題は解決されており、詳細を共有します。
3. 発見されたバグ
これらのバグのほとんどはプリコンパイルされたコントラクトにあり、根本原因と影響はさまざまです。例えば、一部のバグはnonce値の計算に影響を与え、他のバグはガス計算に影響を与え、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 にも同様の問題があることがわかりました。
関連するPRはこちらです。
Moonbeam: ecRecover
このバグは、Ethereumアドレスを復元するために使用されるプリコンパイルされたコントラクト ecRecover にあります。
仕様によると、input[32..63] v は U256 識別子であり、27または28のいずれかであると予想されます。そうでない場合、ecRecover は何も返すべきではありません(ただし、トランザクション全体がロールバックすべきではありません)。
しかし、Moonbeamはここで2つの間違いを犯しています。
- input[32..63] を U256 型に変換してから値をチェックするのではなく、input[63] のみを確認しています。
- 入力が 0 または 1 の場合、有効と見なされます(27または28のみであるべきです)。

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

しかし、Moonbeamは上記の要件を満たさない場合でもトランザクションをロールバックしません。
関連するPRはこちらです。
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が特定のケースで予想よりもはるかに少ないガス料金を請求することを意味します。
関連する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の実装もこのアルゴリズムに従っています。
しかし、仕様によると、ガス計算式は exponent の上位32バイトを使用する必要があります。

関連するPRはこちらです。
3.3 Nonceインクリメントバグ
外部所有アカウント(EOA)のnonceは、そのアドレスによって署名された成功したトランザクションの数を示します。しかし、Aurora Engineでは無効なトランザクションを送信することによってnonceが増加する可能性があることに気づきました。
EIP-1559によると、トランザクションを実行する前に、EVMは署名者が転送されるネイティブトークン(例: ETH)と必要なガスをカバーするのに十分な残高を持っていることを確認する必要があります。そうでない場合、トランザクションはドロップされ、署名者の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で4件、Moonbeamで4件のバグを特定し、修正することに成功しました。さらに、私たちのテスト手法は、昨年Solanaのrbpf実装で2件の重大な脆弱性(CVE-2021–46102、CVE-2022–23066)を特定するために使用されており、私たちのアプローチの有効性を強調しています。
私たちの体系的なバグ検出アプローチを実装することで、お客様はEVM実装が安全で信頼できるものであると確信できます。私たちは、EVM互換性とセキュリティのための最高水準のソリューションを提供し、お客様がユーザーやステークホルダーとの信頼を構築できるよう支援することに尽力しています。
BlockSecについて
BlockSecチームは、ブロックチェーンエコシステムのセキュリティに焦点を当て、主要なDeFiプロジェクトと協力して製品を保護しています。このチームは、学界および産業界のトップクラスのセキュリティ研究者と経験豊富な専門家によって設立されました。彼らは、著名なカンファレンスで複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのいくつかのゼロデイ攻撃を報告し、影響力の高いセキュリティインシデントの詳細な分析レポートを公開しています。



