
当初は Michal Zalewski [email protected] によって開発されました。
このファイルを読む時間がない場合は、 QuickStartGuide.txt を参照してください。
ファジングは、実世界のソフトウェアにおけるセキュリティ問題を特定するための、 最も強力で実証済みの戦略の1つです。これまでにセキュリティ上重要なソフトウェアで 発見されたリモートコード実行や権限昇格のバグの大部分は、 この手法によるものです。
残念ながら、ファジングは比較的浅い手法でもあります。盲目的でランダムな変異では、 テスト対象コードの特定のコードパスに到達する可能性が非常に低く、 一部の脆弱性はこの手法の及ばない範囲に取り残されます。
この問題を解決しようとする試みは数多く行われてきました。初期のアプローチの1つで あるコーパス蒸留(corpus distillation)は、Tavis Ormandy によって先駆けられました。 この方法は、カバレッジシグナルを利用して、大規模で高品質な候補ファイルの コーパスから興味深いシードのサブセットを選択し、それを 従来の手段でファジングするものです。このアプローチは非常にうまく機能しますが、 そのようなコーパスが容易に入手可能であることが必要です。さらに、ブロックカバレッジの 測定では、プログラムの状態について非常に単純化した理解しか得られず、 長期的にファジングの取り組みを導く上ではあまり役に立ちません。
その他のより高度な研究では、プログラムフロー解析(「concolic execution」)、 シンボリック実行、静的解析などの技術に焦点が当てられてきました。 これらの方法はすべて実験環境では非常に有望ですが、 実用面では信頼性とパフォーマンスの問題を抱える傾向があり、 現時点では「dumb」ファジング技術に代わる実用的な選択肢にはなっていません。
American Fuzzy Lop は、極めてシンプルながら盤石な計装ガイド付き遺伝的アルゴリズムを 備えたブルートフォースファザーです。プログラムの制御フローに対する 微妙で局所的な変化を簡単に検出するために、修正されたエッジカバレッジを 使用します。
少し単純化すると、アルゴリズム全体は次のようにまとめられます。
ユーザーが指定した初期テストケースをキューに読み込む。
キューから次の入力ファイルを取り出す。
テストケースを、プログラムの測定された動作を変えない 最小サイズにトリムする。
バランスが取れ、十分に研究されたさまざまな従来のファジング戦略を 使用してファイルを繰り返し変異させる。
生成された変異のいずれかが、計装によって記録された新しい状態遷移を もたらした場合、変異した出力をキューの新しいエントリとして 追加する。
2に戻る。
発見されたテストケースはまた、より新しく、より高いカバレッジを持つ発見によって 陳腐化したものを除外するために定期的に間引かれ、さらにいくつかの 計装駆動の労力最小化ステップを受けます。
ファジングプロセスの副次的な結果として、このツールは興味深いテストケースの 小規模で自己完結したコーパスを作成します。これらは、労力やリソースを 大量に消費する他のテスト体制のシードとして非常に役立ちます。例えば、 ブラウザ、オフィスアプリケーション、グラフィックススイート、または クローズドソースツールのストレステストなどです。
このファザーは、すぐに使えるパフォーマンスが盲目的ファジングや カバレッジのみのツールをはるかに上回ることを保証するため、徹底的にテストされています。
ソースコードが利用可能な場合、サードパーティコードの標準的なビルドプロセスにおいて gcc や clang のドロップイン置換として機能するコンパニオンツールによって、 計装を注入できます。
この計装によるパフォーマンスへの影響はかなり小さいものです。 afl-fuzz が実装する他の最適化と組み合わせることで、ほとんどのプログラムは 従来のツールと同程度か、それ以上に高速にファジングできます。
ターゲットプログラムを再コンパイルする正しい方法はビルドプロセスの詳細によって 異なる場合がありますが、ほぼ普遍的なアプローチは次のとおりです。```shell $ CC=/path/to/afl/afl-gcc ./configure $ make clean all
C++プログラムの場合、`CXX=/path/to/afl/afl-g++` も設定する必要があります。
clangラッパー(afl-clangおよびafl-clang++)も同様に使用できます。clangユーザーは、llvm_mode/README.llvmで説明されているように、より高性能なインストルメンテーションモードを利用することもできます。
ライブラリをテストする場合、stdinまたはファイルからデータを読み取り、テスト対象ライブラリに渡す単純なプログラムを見つけるか作成する必要があります。そのような場合、この実行可能ファイルをインストルメント化されたライブラリの静的バージョンにリンクするか、実行時に正しい.soファイルが読み込まれるようにすることが不可欠です(通常は`LD_LIBRARY_PATH`を設定します)。最も簡単な方法は静的ビルドで、通常は次のようにして可能です:```shell
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
AFL_HARDEN=1 を設定して 'make' を呼び出すと、CC ラッパーが自動的にコード強化オプションを有効にし、単純なメモリバグを検出しやすくなります。AFL に同梱されているヘルパーライブラリである Libdislocator (libdislocator/README.dislocator を参照) も、ヒープ破壊の問題を発見するのに役立ちます。
PS. ASAN ユーザーは、重要な注意事項について notes_for_asan.txt ファイルを確認することをお勧めします。
ソースコードが 利用できない 場合、ファザーはブラックボックスバイナリに対する高速かつオンザフライのインストルメンテーションを実験的にサポートしています。これは、あまり知られていない "ユーザースペースエミュレーション" モードで動作する QEMU のバージョンによって実現されます。
QEMU は AFL とは別のプロジェクトですが、次のようにしてこの機能を簡単にビルドできます:```shell $ cd qemu_mode $ ./build_qemu_support.sh
追加の手順と注意事項については、qemu_mode/README.qemu を参照してください。
このモードはコンパイル時計装よりも約2〜5倍遅く、
並列化にはあまり適しておらず、その他の癖がある場合もあります。
## 5) 初期テストケースの選択
正しく動作させるには、ファザーは1つ以上の開始ファイルを必要とします。
開始ファイルには、対象アプリケーションが通常期待する入力データの良い例が含まれている必要があります。
基本的なルールは2つあります。
- ファイルは小さく保ってください。1 kB未満が理想的ですが、必須ではありません。
サイズが重要な理由については、[perf_tips.txt](https://github.com/google/afl/blob/HEAD/docs/perf_tips.txt) を参照してください。
- 複数のテストケースを使用するのは、各テストケースが機能的に異なる場合のみにしてください。
50種類の異なるバケーション写真を使って画像ライブラリを
ファズしても意味はありません。
このツールに同梱されている testcases/ サブディレクトリには、
開始ファイルの良い例が多数あります。
PS. スクリーニング用に大規模なデータコーパスが利用できる場合は、
afl-cmin ユーティリティを使用して、機能的に異なるファイルのサブセットを特定するとよいでしょう。
それらのファイルは、対象バイナリ内の異なるコードパスを実行します。
## 6) バイナリのファジング
ファジングプロセス自体は、afl-fuzz ユーティリティによって実行されます。このプログラムは、
初期テストケースが入った読み取り専用ディレクトリ、調査結果を保存するための別の場所、
そしてテスト対象バイナリへのパスを必要とします。
標準入力から直接入力を受け取る対象バイナリの場合、通常の構文は次のとおりです:```shell
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
ファイルから入力を受け取るプログラムの場合、'@@' を使用して、対象のコマンドラインで入力ファイル名を配置する場所をマークします。ファザーがこれを置き換えます:```shell $ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
-fオプションを使用すると、変異したデータを特定のファイルに書き込むこともできます。
これは、プログラムが特定のファイル拡張子などを期待する場合に便利です。
インストルメントされていないバイナリは、QEMUモード(コマンドラインに-Qを追加)または
従来のブラインドファザーモード(-nを指定)でファズできます。
-tおよび-mを使用すると、実行プロセスのデフォルトのタイムアウトとメモリ制限を上書きできます。
これらの設定を調整する必要があるまれなターゲットの例としては、コンパイラや
ビデオデコーダがあります。
ファジングパフォーマンスを最適化するためのヒントは、[perf_tips.txt](https://github.com/google/afl/blob/HEAD/docs/perf_tips.txt)で説明されています。
afl-fuzzは、最初に一連の決定論的ファジングステップを実行することに注意してください。
これには数日かかる場合がありますが、きれいなテストケースを生成する傾向があります。
zzufや他の従来のファザーと同様に、すぐに手っ取り早い結果が欲しい場合は、
コマンドラインに-dオプションを追加してください。
## 7) 出力の解釈
[status_screen.txt](https://github.com/google/afl/blob/HEAD/docs/status_screen.txt)ファイルには、表示された統計情報の解釈方法と
プロセスの健全性を監視する方法に関する情報が記載されています。
特にUI要素が赤でハイライトされている場合は、必ずこのファイルを
確認してください。
ファジングプロセスは、Ctrl-Cを押すまで継続されます。最低でも、
ファザーに1回のキューサイクルを完了させる必要があります。これには、
数時間から1週間程度かかることがあります。
出力ディレクトリ内には、次の3つのサブディレクトリが作成され、
リアルタイムで更新されます:
- queue/ - すべての特徴的な実行パスに対するテストケースと、ユーザーが指定した開始ファイル
が含まれます。これは、セクション2で説明した合成コーパス
です。
このコーパスを他の目的で使用する前に、afl-cminツールを使用してより小さなサイズに縮小できます。
このツールは、同等のエッジカバレッジを提供するより小さなファイルのサブセットを
見つけます。
- crashes/ - テスト対象プログラムが致命的なシグナル(例: SIGSEGV、SIGILL、SIGABRT)を
受信する原因となる一意のテストケース。エントリは受信したシグナルごとに
グループ化されます。
- hangs/ - テスト対象プログラムがタイムアウトする原因となる一意のテストケース。
何かがハングアップと分類されるまでのデフォルトの時間制限は、1秒と-tパラメータの値の
うち大きい方です。
この値はAFL_HANG_TMOUTを設定することで微調整できますが、
これが必要になることはほとんどありません。
クラッシュとハングは、関連する実行パスに、以前に記録された障害では見られなかった
状態遷移が含まれる場合に「一意」と見なされます。
単一のバグに複数の方法で到達できる場合、プロセスの初期にはカウントが膨らむことがありますが、
すぐに落ち着くはずです。
クラッシュとハングのファイル名は、親の、障害を発生させないキューエントリと
関連付けられています。これはデバッグに役立ちます。
afl-fuzzによって発見されたクラッシュを再現できない場合、最も可能性の高い原因は、
ツールが使用するのと同じメモリ制限を設定していないことです。試してください:```shell
$ LIMIT_MB=50
$ ( ulimit -Sv $[LIMIT_MB << 10]; /path/to/tested_binary ... )
LIMIT_MB を afl-fuzz に渡す -m パラメータに合わせて変更してください。OpenBSD では、 -Sv も -Sd に変更してください。
既存の出力ディレクトリは、中断されたジョブの再開にも使用できます。試してください:```shell $ ./afl-fuzz -i- -o existing_output_dir [...etc...]
gnuplotがインストールされていれば、afl-plotを使って実行中のファジングタスクのきれいなグラフを生成することもできます。実際の例としては、[http://lcamtuf.coredump.cx/afl/plot/](http://lcamtuf.coredump.cx/afl/plot/)を参照してください。
## 8) 並列ファジング
各afl-fuzzインスタンスはおよそ1コアを占有します。つまり、マルチコアシステムではハードウェアを完全に活用するために並列化が必要です。複数のコアや複数のネットワーク接続されたマシン上で共通のターゲットをファジングする方法のヒントについては、[parallel_fuzzing.txt](https://github.com/google/afl/blob/HEAD/docs/parallel_fuzzing.txt)を参照してください。
並列ファジングモードは、AFLを他のファザー、シンボリック実行エンジン、コンコリック実行エンジンなどと連携させる簡単な方法も提供します。これについても、[parallel_fuzzing.txt](https://github.com/google/afl/blob/HEAD/docs/parallel_fuzzing.txt)の最後のセクションを参照してください。
## 9) ファザーディクショナリ
デフォルトでは、afl-fuzzのミューテーションエンジンはコンパクトなデータ形式(画像、マルチメディア、圧縮データ、正規表現構文、シェルスクリプトなど)向けに最適化されています。特に冗長で反復的な表現が多い言語(代表的なものとしてHTML、SQL、JavaScriptなど)にはあまり適していません。
構文を認識するツールを構築する手間を避けるため、afl-fuzzは、対象となるデータ型に関連する言語キーワード、マジックヘッダー、その他の特殊トークンのオプションのディクショナリをファジングプロセスにシードし、それを使って基礎となる文法をその場で再構築する方法を提供します:
[http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html](http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html)
この機能を使用するには、まずdictionaries/README.dictionariesで説明されている2つの形式のいずれかでディクショナリを作成し、次にコマンドラインの-xオプションでファザーにそれを指定する必要があります。
(そのサブディレクトリには、いくつかの一般的なディクショナリもすでに用意されています。)
基礎となる構文のより構造化された記述を提供する方法はありませんが、ファザーはインストルメンテーションフィードバックだけに基づいてその一部を推測できる可能性が高いです。これは実際に機能します。例えば:
[http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html](http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html)
追記:明示的なディクショナリが指定されていない場合でも、afl-fuzzは決定論的バイトフリップ中にインストルメンテーションを非常に注意深く監視することで、入力コーパス内の既存の構文トークンを抽出しようとします。これは一部のタイプのパーサーや文法で機能しますが、-xモードほどではありません。
ディクショナリをどうしても入手できない場合は、AFLをしばらく実行してから、AFLに付属のユーティリティであるトークンキャプチャライブラリを使用するという別の方法もあります。これについては、libtokencap/README.tokencapを参照してください。
## 10) クラッシュのトリアージ
カバレッジベースでクラッシュをグループ化すると、通常、手動または非常にシンプルなGDBやValgrindスクリプトで迅速にトリアージできる小さなデータセットが生成されます。また、各クラッシュはキュー内の親となる非クラッシュテストケースに遡ることができるため、障害の診断が容易になります。
とはいえ、一部のファジングクラッシュは、多くのデバッグとコード分析作業なしに悪用可能性を迅速に評価することが難しい場合があることを認識しておくことが重要です。この作業を支援するために、afl-fuzzは-Cフラグで有効になる非常にユニークな「クラッシュ探索」モードをサポートしています。
このモードでは、ファザーは1つ以上のクラッシュするテストケースを入力として受け取り、フィードバック駆動型のファジング戦略を使用して、プログラムをクラッシュ状態に保ちながら到達可能なすべてのコードパスを非常に迅速に列挙します。
クラッシュを引き起こさないミューテーションは拒否されます。実行パスに影響を与えない変更も同様に拒否されます。
出力は小さなファイルのコーパスであり、攻撃者がフォールトアドレスに対してどの程度の制御を持っているか、初期の境界外読み取りをすり抜けることが可能かどうか、そしてその先に何があるかを確認するために非常に迅速に調査できます。
ああ、もう1つ:テストケースの最小化にはafl-tminをお試しください。このツールは非常に簡単な方法で操作できます:```shell
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
このツールは、クラッシュするテストケースとクラッシュしないテストケースの両方で動作します。クラッシュ モードでは、計装済みバイナリと非計装バイナリの両方を問題なく受け入れます。 非クラッシュモードでは、ミニマイザーは標準のAFL計装を利用して、 実行パスを変更せずにファイルをより単純にします。
ミニマイザーは、-m、-t、-f、および@@構文を、afl-fuzzと互換性のある方法で 受け付けます。
AFLへの最近の追加のもう1つは、afl-analyzeツールです。これは入力 ファイルを受け取り、バイトを順次反転させ、テスト対象プログラムの動作を 観察します。そして、どのセクションが重要であるように見え、どのセクションが 重要でないかに基づいて入力を色分けします。完璧ではありませんが、複雑な ファイル形式について迅速な洞察を提供できることがよくあります。その動作の詳細は、 technical_details.txtの末尾の近くにあります。
ファジングは、クラッシュしない設計エラーや実装エラーを発見するための、素晴らしく 過小評価されている手法でもあります。かなり多くの興味深いバグが、 対象プログラムを変更して、たとえば次の場合にabort()を呼び出すようにすることで発見されてきました:
同じファザー生成入力を与えられたときに、2つのbignumライブラリが異なる出力を 生成する場合、
同じ入力画像を連続して複数回デコードするように求められたときに、画像ライブラリが異なる出力を 生成する場合、
シリアライズ / デシリアライズライブラリが、ファザーが供給したデータを反復的にシリアライズおよび デシリアライズする際に安定した出力を生成できない場合、
圧縮ライブラリが、特定のblobを圧縮してから解凍するように求められたときに、入力ファイルと 矛盾する出力を生成する場合。
これらのチェックまたは同様の健全性チェックの実装には、通常ほとんど時間がかかりません。
特定のパッケージのメンテナーであれば、このコードを#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
(libfuzzerとも共有されるフラグ) または#ifdef __AFL_COMPILER (これはAFL専用) で
条件付きにすることができます。
他の多くの計算集約的なタスクと同様に、ファジングはハードウェアとOSに負担をかける可能性があることに 留意してください。特に:
CPUは高温になり、適切な冷却が必要になります。ほとんどの場合、冷却が不十分だったり、 正常に動作しなくなったりすると、CPU速度は自動的に制限されます。 とはいえ、特にあまり適さないハードウェア(ノートPC、スマートフォンなど)で ファジングを行う場合、 何かが故障してしまう可能性も完全にはあり得ないわけではありません。
対象プログラムが、不安定にギガバイト単位のメモリを消費したり、ジャンクファイルで ディスクスペースを埋め尽くしたりする可能性があります。AFLは基本的なメモリ制限を 強制しようとしますが、あらゆる可能性のある事故を防ぐことはできません。 結論として、データ損失の可能性が許容リスクではないシステムでファジングを 行うべきではありません。
ファジングには、ファイルシステムへの数十億回の読み書きが伴います。最新のシステムでは、 これは通常、大量にキャッシュされるため、"物理的な" I/Oはかなり控えめになりますが、 このバランスを変える要因は数多くあります。潜在的な問題を監視するのはあなたの責任です。 非常に重いI/Oが発生すると、多くのHDDやSSDの寿命が短くなる 可能性があります。
LinuxでディスクI/Oを監視する良い方法は、'iostat'コマンドです:```shell $ iostat -d 3 -x -k [...optional disk ID...]
## 13) 既知の制限事項と改善の余地
AFLに関する最も重要な注意点の一部を以下に示します。
- AFLは、最初に生成されたプロセスがシグナル(SIGSEGV、SIGABRTなど)によって終了するかどうかをチェックして障害を検出します。
これらのシグナルにカスタムハンドラをインストールするプログラムでは、関連するコードをコメントアウトする必要がある場合があります。
同様に、ファジング対象が生成した子プロセスでの障害は、
それを捕捉するコードを手動で追加しない限り、
検出を免れる可能性があります。
- 他のブルートフォースツールと同様に、暗号化、チェックサム、暗号署名、または圧縮が
テスト対象の実際のデータ形式を完全にラップするために使用されている場合、
ファザーのカバレッジは限定的です。
これを回避するには、関連するチェックをコメントアウトできます
(参考として experimental/libpng_no_checksum/ を参照)。それが不可能な場合は、
experimental/post_library/ で説明されているように、
ポストプロセッサを作成することもできます。
- ASANと64ビットバイナリには、いくつか残念なトレードオフがあります。これは
afl-fuzzの特定の欠陥によるものではありません。[notes_for_asan.txt](https://github.com/google/afl/blob/HEAD/docs/notes_for_asan.txt) を参照
してヒントを得てください。
- ネットワークサービス、バックグラウンドデーモン、またはUI操作を必要とするインタラクティブアプリの
ファジングは直接サポートされていません。それらをより従来通りの方法で動作させるには、
簡単なコード変更が必要になる場合があります。Preenyも比較的簡単な選択肢を提供するかもしれません -
参照:
https://github.com/zardus/preeny
ネットワークベースのサービスを変更するための有用なヒントは、次の場所にもあります:
https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop
- AFLは人間が読める形式のカバレッジデータを出力しません。カバレッジを監視したい場合は、
Michael Rashのafl-covを使用してください: https://github.com/mrash/afl-cov
- 時折、自我を持った機械が創造主に反抗することがあります。もしそうなった場合は、
http://lcamtuf.coredump.cx/prep/ を参照してください。
この他に、プラットフォーム固有のヒントについては INSTALL を参照してください。
## 14) 謝辞
afl-fuzzへの改良の多くは、以下からのフィードバック、バグ報告、またはパッチがなければ
実現できなかったでしょう:```
Jann Horn Hanno Boeck
Felix Groebert Jakub Wilk
Richard W. M. Jones Alexander Cherepanov
Tom Ritter Hovik Manucharyan
Sebastian Roschke Eberhard Mattes
Padraig Brady Ben Laurie
@dronesec Luca Barbato
Tobias Ospelt Thomas Jarosch
Martin Carpenter Mudge Zatko
Joe Zbiciak Ryan Govostes
Michael Rash William Robinet
Jonathan Gray Filipe Cabecinhas
Nico Weber Jodie Cunningham
Andrew Griffiths Parker Thompson
Jonathan Neuschfer Tyler Nighswander
Ben Nagy Samir Aguiar
Aidan Thornton Aleksandar Nikolich
Sam Hakim Laszlo Szekeres
David A. Wheeler Turo Lamminen
Andreas Stieger Richard Godbee
Louis Dassy teor2345
Alex Moneger Dmitry Vyukov
Keegan McAllister Kostya Serebryany
Richo Healey Martijn Bogaard
rc0r Jonathan Foote
Christian Holler Dominique Pelle
Jacek Wielemborek Leo Barnes
Jeremy Barnes Jeff Trull
Guillaume Endignoux ilovezfs
Daniel Godas-Lopez Franjo Ivancic
Austin Seipp Daniel Komaromy
Daniel Binderman Jonathan Metzman
Vegard Nossum Jan Kneschke
Kurt Roeckx Marcel Bohme
Van-Thuan Pham Abhik Roychoudhury
Joshua J. Drake Toby Hutton
Rene Freingruber Sergey Davidoff
Sami Liedes Craig Young
Andrzej Jackowski Daniel Hodson
ありがとうございます!
質問は? 懸念事項は? バグ報告は? GitHubをご利用ください。
プロジェクトにはメーリングリストもあります。参加するには、以下のアドレスにメールを送信してください。 [email protected]。または、先にアーカイブを閲覧したい場合は、 こちらを試してください: https://groups.google.com/group/afl-users。