
フォールトインジェクションベースのファザーで、ジェネレータプログラムを変異させてターゲット用のほぼ有効な入力を生成し、複雑なフォーマットや暗号保護されたデータを文法近似なしでファジングできるようにします。
Fuzztruction は、直接入力を突然変異させる(ほとんどのファザーが行うように)のではなく、いわゆるジェネレータアプリケーションを使用してファジング対象の入力を生成するファザーの学術プロトタイプです。データを生成するプログラムは通常、正しい表現を生成するため、我々のファザーはジェネレータプログラムを(障害を注入することで)突然変異させ、生成されるデータがほぼ有効になるようにします。理想的には、生成されたデータはファジング対象(コンシューマと呼びます)の解析段階を通過し、より深いプログラムロジックで予期しない動作を引き起こします。これにより、暗号化やメッセージ整合性コードなどの暗号プリミティブを利用するターゲットでもファジングを可能にします。このアプローチの主な利点は、重いプログラム解析技術や文法近似、人間の介入を必要とせずに複雑なデータを生成できることです。
詳細については、論文を参照してください。引用には、以下のBibTeXエントリを使用できます。
@inproceedings{bars2023fuzztruction,
title={Fuzztruction: Using Fault Injection-based Fuzzing to Leverage Implicit Domain Knowledge},
booktitle = {32st USENIX Security Symposium (USENIX Security 23)},
publisher = {USENIX Association},
year={2023},
author={Bars, Nils and Schloegel, Moritz and Scharnowski, Tobias and Schiller, Nico and Holz, Thorsten},
}
論文の実験を再現する手順については、このドキュメントを読んだ後にfuzztruction-experimentsサブモジュールのドキュメントを参照してください。
互換性: 私たちはプロトタイプを可能な限りプラットフォームに依存しないように努めていますが、すべてのプラットフォームでテストすることはできません。そのため、問題が発生した場合は、開発中にホストシステムとして使用されていた Ubuntu 22.04.1 を使用してください。
# リポジトリをクローン
git clone --recurse-submodules https://github.com/fuzztruction/fuzztruction.git
# オプション1: プリビルド版のランタイム環境を取得
# 論文の実験を簡単に再現するために、プリビルド環境の使用をお勧めします(約30GBのデータがダウンロードされます)
# 結果を再現せず、独自のターゲットをファジングしたい場合は使用しないでください(代わりに次のコマンドを使用してください)。
./env/pull-prebuilt.sh
# オプション2: Fuzztructionのランタイム環境をゼロからビルド
# pull-prebuilt.sh を実行した場合は、このコマンドを実行しないでください。
./env/build.sh
# 上記でビルド/プルしたイメージに基づいてコンテナを起動
# プリビルドイメージを使用する場合(上記でプルした場合)は、
# USE_PREBUILT を 1 に設定する必要があります(例: `USE_PREBUILT=1 ./env/start.sh`)
./env/start.sh
# このスクリプトを再度呼び出すと、コンテナ内でシェルが起動します。
# (同じコンテナ内で複数のシェルを起動するために複数回呼び出すことが可能です)。
./env/start.sh
# 2回目の start.sh の実行で、ファザーが自動的にビルドされます。
# 以下の「Fuzztructionを使用したターゲットのファジング」を参照してください。
Fuzztruction には、以下のコアコンポーネントが含まれています。
スケジューラは、ジェネレータとコンシューマの相互作用を調整します。ファジングキャンペーンを管理し、主なタスクはファジングループを組織化することです。また、キューエントリを含むキューを維持します。各エントリは、ジェネレータに渡されるシード入力(存在する場合)と、ジェネレータに適用されたすべての突然変異で構成されます。各キューエントリは単一のテストケースを表します。従来のファジングでは、このようなテストケースは単一のファイルとして表現されます。スケジューラの実装は scheduler ディレクトリにあります。
ジェネレータは、ファジング対象(コンシューマ)に合わせた入力を生成するシードジェネレータと考えることができます。一般的なファジングアプローチがビットレベルの突然変異で入力を直接変異させるのに対し、我々はジェネレータプログラムに障害を注入することで間接的に入力を変異させます。より正確には、ジェネレータが出力を生成する際に使用するデータ操作を特定し、変異させます。このアプローチを実現するには、ファジング対象が期待する入力形式に一致する出力を生成するプログラムが必要です。
ジェネレータの実装は generator ディレクトリにあります。以下で説明する2つのコンポーネントで構成されています。
コンパイラパス(generator/pass)は、いわゆるパッチポイントを使用してターゲットを計装します。現在の(LLVM12以下でテスト済み)この機能の実装は不安定なため、我々は LLVM にパッチを適用して使用可能にしています。パッチは llvm リポジトリ(ここではサブモジュールとして含まれています)にあります。パッチは実験的であり、本番環境での使用を意図していないことに注意してください。
パッチポイントの位置は、コンパイルされたバイナリ内の別のセクションに記録されます。このセクションの解析に関するコードは lib/llvm-stackmap-rs にあり、crates.io でも公開しています。
ファジング中、スケジューラはパッチポイントのセットからターゲットを選択し、その決定をエージェント(後述)に渡します。エージェントは、指定されたパッチポイントに対して目的の突然変異を適用する責任を負います。
generator/agent に実装されているエージェントは、カスタムコンパイラパスでコンパイルされたジェネレータアプリケーションのコンテキスト内で実行されます。主なタスクは、フォークサーバの実装とスケジューラとの通信です。スケジューラから共有メモリとメッセージキューを介して渡された命令に基づき、エージェントは JIT エンジンを使用してジェネレータを変異させます。
ジェネレータの対となるのがコンシューマです。これは、ジェネレータが生成した入力を消費するファジング対象です。Fuzztruction では、コンシューマアプリケーションを AFL++ のコンパイラパスでコンパイルするだけで十分です。このパスを使用してカバレッジフィードバックを記録します。このフィードバックが、ジェネレータの突然変異のガイドとなります。
Fuzztruction を使用する前に、Docker イメージとして提供されるランタイム環境が必要です。このイメージは、自分でビルドするか、プリビルド版をプルすることで入手できます。両方の方法を以下に説明します。ランタイム環境を準備する前に、このリポジトリとすべてのサブリポジトリをクローンする必要があります。
git clone --recurse-submodules https://github.com/fuzztruction/fuzztruction.git
Fuzztruction のランタイム環境は、env/build.sh を実行することでビルドできます。これにより、Fuzztruction の完全なランタイム環境を含む Docker イメージがローカルにビルドされます。デフォルトでは、パッチ適用済み LLVM のプリビルド版が使用され、Docker Hub からプルされます。ローカルでビルドした LLVM バージョンを使用する場合は、llvm ディレクトリを確認してください。
ほとんどの場合、プリビルド環境を使用する特別な理由はありません。ただし、論文で行われた正確な実験を再現したい場合を除きます。プリビルドイメージは、プリビルドされた評価ターゲットとすべての依存関係を含むすべてを提供します。イメージは、env/pull-prebuilt.sh を実行して取得できます。
以下のセクションでは、ローカルでビルドしたイメージまたはプリビルドイメージに基づいてランタイム環境を起動する方法を説明します。論文の実験の再現に関する詳細は、fuzztruction-experiments サブモジュールにあります。
ランタイム環境をビルドまたはプリビルド版をプルした後、ファザーを使用する準備が整います。ファザーの環境ライフサイクルは、env フォルダにあるスクリプト群によって管理されます。
start.sh を使用すると、コンテナ内で任意の数のシェルを起動できます。Visual Studio Code のコンテナ拡張機能を使用すると、Docker コンテナ内で便利に作業できます。
データ交換を容易にするために、いくつかのファイル/フォルダがホストからコンテナにマウントされます。ランタイム環境の詳細については、次のセクションで説明します。
このセクションでは、Fuzztruction と共に提供されるランタイム環境(Docker コンテナ)について詳しく説明します。コンテナ内のユーザーは user という名前で、デフォルトでパスワードなしの sudo アクセス権限を持っています。
パーミッション: Docker イメージのユーザー名は
userで、イメージを最初にビルドしたユーザーと同じユーザー ID(UID)を持っています。そのため、ホストからのマウントはコンテナ内でアクセスできます。ただし、プリビルドイメージを使用する場合は、イメージが別のマシンでビルドされているため、この限りではありません。ホストとデータを交換する際には注意が必要です。
コンテナ内では、以下のパスがホストから(バインド)マウントされています。
Docker ランタイム環境をビルドし、コンテナを起動した後、Fuzztraction バイナリ自体をビルドする必要があります。./env/start.sh を使用してコンテナ内でシェルを起動すると、ビルドプロセスが自動的にトリガーされます。そのため、次のセクションの手順は主に、コードに変更を加えた後に Fuzztruction を再ビルドしたい人のためのものです。
Fuzztruction をビルドするには、/home/user/fuzztruction で cargo build を呼び出すだけで十分です。これにより、コンポーネントセクションで説明したすべてのコンポーネントがビルドされます。最も重要なビルド成果物は次のとおりです。
libpng を例として、Fuzztruction の機能を紹介します。libpng は比較的小さく、外部依存関係がないため、以下の手順でプリビルドイメージを使用する必要はありません。ただし、特にモバイル CPU では、競合のないカバレッジマップエンコーディング機能と比較分割のために、AFL++ バイナリのビルドプロセスに数時間かかる場合があります。
プリビルド: プリビルド版を使用する場合、ビルドは不要であり、この手順はスキップできます。
fuzztruction-experiments/comparison-with-state-of-the-art/binaries/ ディレクトリに移動し、./build.sh libpng を実行します。これにより、ソースがプルされ、libpng/config.sh で定義された手順に従ってビルドが開始されます。
以下のコマンド
sudo ./target/debug/fuzztruction fuzztruction-experiments/comparison-with-state-of-the-art/configurations/pngtopng_pngtopng/pngtopng-pngtopng.yml --purge --show-output benchmark -i 100
を使用すると、ターゲットが機能するかどうかをテストできます。各ターゲットは YAML 設定ファイルで定義されています。設定ファイルは configurations ディレクトリにあり、独自の設定を作成するための良い出発点になります。pngtopng-pngtopng.yml ファイルには詳細なドキュメントが記載されています。
ファザーがエラーで終了した場合、デバッグを支援する方法がいくつかあります。
fuzztruction に --show-output を渡すと、ジェネレータとコンシューマの stdout/stderr を観察できます(データの受け渡しや読み取りに使用されていない場合)。sink の env セクションで AFL_DEBUG を設定すると、コンシューマに関するより詳細な出力が得られる場合があります。LD_PRELOAD を使用する場合は、指定されたパスを再確認してください。ファジングプロセスを開始するには、次のコマンドを実行するだけで十分です。
sudo ./target/debug/fuzztruction ./fuzztruction-experiments/comparison-with-state-of-the-art/configurations/pngtopng_pngtopng/pngtopng-pngtopng.yml fuzz -j 10 -t 10m
これにより、10 コアでファジングが開始され、タイムアウトは 10 分です。ファザーによって生成された出力は、ターゲットの設定ファイルの work-directory 属性で定義されたディレクトリに保存されます。pngtopng の場合、デフォルトの場所は /tmp/pngtopng-pngtopng です。
ワーキングディレクトリが既に存在する場合は、再実行を許可するために --purge を fuzztruction の引数として渡す必要があります。このフラグはサブコマンドの前、つまり fuzz や benchmark の前に渡す必要があります。
Fuzztruction と一緒に AFL++ を実行するには、aflpp サブコマンドを使用して、実行中に Fuzztruction が見つけた入力で再シードされる AFL++ ワーカーを起動できます。上記のコマンドで Fuzztruction が実行されたと仮定すると、次のコマンドを実行するだけで十分です。
sudo ./target/debug/fuzztruction ./fuzztruction-experiments/comparison-with-state-of-the-art/configurations/pngtopng_pngtopng/pngtopng-pngtopng.yml aflpp -j 10 -t 10m
これにより、10 の AFL++ プロセスが起動され、10 分後に終了します。Fuzztruction と AFL++ によって見つけられた入力は、ワーキングディレクトリの interesting フォルダに定期的に同期されます。AFL++ を独立して実行するが、同じ .yml 設定ファイルに基づいて実行する場合は、--suffix 引数を使用して、起動されたファザーのワーキングディレクトリにサフィックスを追加できます。
ファジング実行が終了した後、tracer サブコマンドを使用すると、ファジング中に見つかったすべての興味深い入力に対してカバーされた基本ブロックのリストを取得できます。これらのトレースは、ワーキングディレクトリ内の traces サブディレクトリに保存されます。各トレースには、実行中に実行されたすべての基本ブロックのアドレス(実行順)の zlib 圧縮された JSON オブジェクトが含まれています。さらに、アドレスを実際の ELF ファイルにマッピングするためのメタデータも提供されます。
./target/debug/coverage にある coverage ツールを使用して、収集されたデータをさらに処理できます。Fuzztruction によって作成されたワーキングディレクトリを含むトップレベルディレクトリ(前の例では /tmp など)を渡す必要があります。./target/debug/coverage /tmp を実行すると、時間をカバーされた基本ブロックの数にマッピングする .csv ファイルと、タイムスタンプを見つかった基本ブロックアドレスのセットにマッピングする .json ファイルが生成されます。どちらのファイルも、特定のファジング実行のワーキングディレクトリにあります。
./env/start.sh新しいコンテナを起動するか、既に実行中のコンテナにシェルを起動します。プリビルド: USE_PREBUILT=1 をエクスポートすると、プリビルド環境に基づくコンテナが起動します。プリビルドからローカルビルド、またはその逆に切り替える場合は、最初に stop.sh を実行する必要があります。 |
./env/stop.sh | コンテナを停止します。イメージを再ビルドした後は、これを呼び出すことを忘れないでください。 |
| コンテナ内パス | ホスト上のパス | 注記 |
|---|
/home/user/fuzztruction | ./ | プリビルド: プリビルドイメージを使用する場合、このフォルダはイメージの一部です。そのため、変更はホストに反映されません。 |
/home/user/shared | ./ | ホストとのデータ交換に使用されます。 |
/home/user/.zshrc | ./data/zshrc | - |
/home/user/.zsh_history | ./data/zsh_history | - |
/home/user/.bash_history | ./data/bash_history | - |
/home/user/.config/nvim/init.vim | ./data/init.vim | - |
/home/user/.config/Code | ./data/vscode-data | Visual Studio Code の設定をコンテナ再起動間で永続化するために使用されます。 |
/ssh-agent | $SSH_AUTH_SOCK | ホスト上で SSH エージェントが実行されている場合、コンテナ内で使用できるようにします。 |
/home/user/.gitconfig | /home/$USER/.gitconfig | ホストの gitconfig が存在する場合に使用します。 |
/ccache | ./data/ccache | ccache キャッシュをコンテナ再起動間で永続化するために使用されます。 |
| 成果物 | 説明 |
|---|
./generator/pass/fuzztruction-source-llvm-pass.so | ジェネレータアプリケーションにパッチポイントを挿入するために使用される LLVM パス。注: パスの場所は /etc/ld.so.conf.d/fuzztruction.conf に記録されているため、コンパイラはコンパイル中にパスを見つけることができます。パスが見つからないという問題が発生した場合は、sudo ldconfig を実行し、新しく起動したシェルで再試行してください。 |
./generator/pass/fuzztruction-source-clang-fast | ジェネレータアプリケーションをコンパイルするためのコンパイララッパー。このラッパーはカスタムコンパイラパスを使用し、ターゲットをエージェントにリンクし、エージェントの init メソッドへの呼び出しをジェネレータの main に注入します。 |
./target/debug/libgenerator_agent.so | ジェネレータアプリケーションに注入されるエージェント。 |
./target/debug/fuzztruction | 実際のファザーを表す fuzztruction バイナリ。 |