EVM互換性とセキュリティを維持するための体系的なアプローチ

EVM(イーサリアム仮想マシン)互換ブロックチェーンは、イーサリアムブロックチェーンのスマートコントラクト機能、プログラミング言語(Solidity)、およびツールエコシステムと互換性があるように設計されています。

EVM互換性とセキュリティを維持するための体系的なアプローチ

EVM(Ethereum Virtual Machine)互換ブロックチェーンは、Ethereumブロックチェーンのスマートコントラクト機能、プログラミング言語(Solidity)、およびツールのエコシステムとの互換性を持つように設計されています。このプロセスにおいて、EVM準拠の仮想マシンの実装は重要なステップの1つです。しかし、私たちの調査では、さまざまな実装におけるEVMの互換性を維持することは容易ではないことがわかりました。

この問題の解決策として、BlockSecはEVM実装内部のバグやセキュリティ脆弱性を体系的に特定できる内部システムを開発しました。このシステムは効果的であることが証明されました。Aurora Engineで4つのバグ、Moonbeamで4つのバグが発見され、すべて報告および修正されました。なお、このテスト手法は昨年、Solana rbpf実装における2つの重大な脆弱性(CVE-2021–46102CVE-2022–23066)の特定にも使用されました。

1. 背景

現在、低ガス手数料、高スループット、優れたパフォーマンスに最適化されたさまざまなブロックチェーンが提案されています。この場合、新しい仮想マシンや開発言語が提案され、元のSolidity開発者にとっては移行のハードルが高くなります。このような状況で、多くのEVM互換ソリューションが提案されており、ユーザーはこれらの新しいチェーン上でSolidityで開発したDAppをデプロイできます。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はパディング操作を実行せずにロールバックします。

また、プリコンパイルされたコントラクト ecAddmodexp にも同様の問題があることがわかりました。

関連する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が特定のケースで予想よりもはるかに少ないガス手数料を請求することを意味します。

関連する問題へのリンクはこちらです。

Moonbeam: modexp

バグは、プリコンパイルされたコントラクト modexpcalculate_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アプリケーションのいくつかのゼロデイ攻撃を報告し、影響の大きいセキュリティインシデントの詳細な分析レポートをリリースしました。

公式ウェブサイト | Twitter | Medium

Sign up for the latest updates