動的言語インタプリタのための(カバレッジガイド付き)ファザーで、カスタム中間言語("FuzzIL")に基づいており、これを変異させてJavaScriptに変換できます。
このファザーを使用する基本的な手順は次のとおりです:
swift build [-c release]。swift run [-c release] FuzzilliCli --profile=<profile> [other cli options] /path/to/jsshell。swift run FuzzilliCli --helpも参照してください。Docker内およびGoogle Compute Engine上でのFuzzilliとサポートされているJavaScriptエンジンのビルドと実行もサポートされています。
main.swiftをチェックして、Fuzzilliライブラリの使用例を確認し、さまざまな設定オプションを試してみてください。次に、Fuzzer.swiftを見て、高レベルのファジングロジックを理解してください。そこから、興味のある部分に飛び込んでください。
このプロジェクトへのパッチ、追加、その他のコントリビューションを大歓迎します!ただし、コントリビューター向け注意事項をひと通り確認してください。FuzzilliはおおよそGoogleのSwiftコードスタイルガイドに従っています。
このプロジェクトの助けを借りて発見された脆弱性について、[email protected]に簡単なメモ(できればCVE番号を含めて)を送っていただくか、プルリクエストを開いていただけると、バグショーケースセクションに含めることができ、大変ありがたく思います。それ以外にも、脆弱性に対するバグ報奨金やCVEクレジットなどを申請していただいてももちろん構いません:)
JITコンパイラなどのコアインタプリタのバグをファジングする場合、生成されたプログラムの意味的正確性が問題になります。これは、ランタイムAPIのファジングなど他のほとんどのシナリオとは対照的で、その場合は生成されたコードをtry-catch構文でラップすることで意味的正確性を簡単に回避できます。意味的に正しいサンプルを許容可能な割合で生成するにはいくつかの方法があり、その1つがコーパス内の全サンプルが意味的にも有効である変異的アプローチです。その場合、各変異によって有効なサンプルが無効になる可能性は低くなります。
変異ベースのJavaScriptファザーを実装するには、JavaScriptコードに対する変異を定義する必要があります。プログラムのASTや他の構文要素を変異させる代わりに、カスタム中間言語(IL)を定義し、その上でプログラムの制御フローやデータフローに対する変異をより直接的に実行します。このILは後で実行のためにJavaScriptに変換されます。中間言語はおおよそ次のようになります:
v0 <− LoadInteger '0'
v1 <− LoadInteger '10'
v2 <− LoadInteger '1'
v3 <− LoadInteger '0'
BeginFor v0, '<', v1, '+', v2 −> v4
v6 <− BinaryOperation v3, '+', v4
Reassign v3, v6
EndFor
v7 <− LoadString 'Result: '
v8 <− BinaryOperation v7, '+', v3
v9 <− LoadGlobal 'console'
v10 <− CallMethod v9, 'log', [v8]
これは例えば、次のJavaScriptコードに簡単に変換できます:
const v0 = 0;
const v1 = 10;
const v2 = 1;
let v3 = 0;
for (let v4 = v0; v4 < v1; v4 = v4 + v2) {
const v6 = v3 + v4;
v3 = v6;
}
const v7 = "Result: ";
const v8 = v7 + v3;
const v9 = console;
const v10 = v9.log(v8);
または、中間式をインライン化して次のJavaScriptコードに変換することもできます:
let v3 = 0;
for (let v4 = 0; v4 < 10; v4++) {
v3 = v3 + v4;
}
console.log("Result: " + v3);
FuzzILにはいくつかの特性があります:
Reassign命令などの専用操作を通じてのみ再代入できます。これらのプログラムに対していくつかの変異を実行できます:
Fuzzilliの動作に関するより詳細な議論はこちらにあります。
ファザーはSwiftで実装されており、一部の部分(カバレッジ計測、ソケット通信など)はCで実装されています。
ファザーインスタンス(Fuzzer.swiftに実装)は、以下の中心的なコンポーネントで構成されています:
さらに、オプションでいくつかのモジュールが利用可能です:
ファザーはイベント駆動型で、異なるクラス間のほとんどのやり取りはイベントを通じて行われます。イベントは、例えばクラッシュや興味深いプログラムの発見、新しいプログラムの実行、ログメッセージの生成などの結果としてディスパッチされます。イベントの完全なリストはEvents.swiftを参照してください。イベントメカニズムはファザーのさまざまなコンポーネントを効果的に疎結合し、追加のモジュールを簡単に実装できるようにします。
FuzzILプログラムはProgramBuilderインスタンスを使用して構築できます。ProgramBuilderは、新しい命令の作成と追加、別のプログラムからの命令の追加、既存の変数の取得、現在位置での実行コンテキストの照会(例:ループ内かどうか)などのメソッドを提供します。
FuzzilliはREPRL (read-eval-print-reset-loop)と呼ばれるカスタム実行モードを使用します。そのために、ターゲットエンジンはパイプや共有メモリを介してスクリプト入力を受け入れ、実行し、内部状態をリセットして次のスクリプトを待つように変更されます。これにより、プロセス生成のオーバーヘッドと、大部分のエンジン初期化のオーバーヘッドが除去されます。
ターゲットプロセスごとに1つのFuzzerインスタンスがあります。これにより、プログラムの同期実行が可能になり、連続変異や最小化などのさまざまなアルゴリズムの実装が簡素化されます。さらに、コーパスなどの内部状態へのスレッドセーフなアクセスを実装する必要がなくなります。各ファザーインスタンスは独自のDispatchQueueを持ち、概念的には1つのスレッドに対応します。経験則として、Fuzzerインスタンスとのすべてのやり取りは、そのインスタンスのディスパッチキュー上で行われなければなりません。これにより、キューがシリアルであるためスレッドセーフが保証されます。詳細はドキュメントを参照してください。
スケールするために、ファザーインスタンスはツリー階層を形成でき、その場合、新しく見つかった興味深いサンプルやクラッシュを親ノードに報告します。親ノードはそのコーパスを子ノードと同期します。ツリー内のノード間の通信はさまざまな方法で行われ、それぞれモジュールとして実装されています:
この設計により、ファザーは1台のマシン上の多数のコアや、複数の異なるマシンにスケールできます。1つの親ノードにあまりにも多くのインスタンスがプログラムを送信するとすぐに過負荷になる可能性があるため、複数レベルのインスタンスを設定することが可能です。例えば、1つのルートインスタンス、ルートに接続された16の中間ノード、中間ノードに接続された256の「リーフ」インスタンスなどです。分散ファジングの詳細については、Cloud/ディレクトリを参照してください。
以下は、Fuzzilliの助けを借りて発見されたバグの一部のリストです。このリストには、影響を受けるソフトウェアの少なくともベータリリースに存在していたセキュリティ影響のあるバグのみを含めるべきです。Fuzzilliは開発中の継続的なファズテストによく使用されるため、Fuzzilliによって発見された多くの問題は、脆弱なコードがベータリリースに達する前に発見されることが多いため、このリストには含まれていません。ただし、Fuzzilliによって最近V8で発見されたすべての問題のリストはこちらにあります。
Fuzzilliによって発見されたバグを報告してくれたすべてのユーザーに感謝します!
これは公式にサポートされているGoogle製品ではありません。