ビギナー
Mayhem での非 Docker ターゲットのテスト¶
テスト対象は、Docker イメージに含まれていない通常のバイナリですか? このレッスンでは、Mayhem パッケージを使用して非 Docker ターゲットをパッケージ化し、ファジングする方法を説明します。
学習時間の目安: 30 分
このレッスンを終了すると、以下のことができるようになります。
mayhem package
コマンドを使用してターゲット バイナリをパッケージ化する。testme
ターゲット バイナリに対して Mayhem ランを実行する。- (Mayhemfile を修正せずに) ターゲットを置換してリグレッション テストをセットアップして実行する。
- パッケージ化されたターゲットに対してテスト ケースをシード化する。
レッスンを駆け足で
始める前に前提条件を確認してください。
-
mayhem package
コマンドを使用してtestme
バイナリをパッケージ化します。mayhem package testme -o /tmp/testme-pkg
-
testme
バイナリの Mayhemfile を構成します。1 2 3 4 5 6
project: testme target: testme duration: 90 advanced_triage: true cmds: - cmd: /root/tutorial/testme/v1/testme @@
-
mayhem run
コマンドを使用してtestme
バイナリに対して Mayhem ランを実行します。mayhem run /tmp/testme-pkg
以下が必要です。
- Mayhem CLI がインストールされ、Mayhem サーバーで認証されていること
-
Linux OS (macOS および Windows は
mayhem package
を完全にはサポートしません)そのため、Linux 環境として
forallsecure/tutorial
Docker イメージを使用して続けることを推奨します。docker pull forallsecure/tutorial:2.10 docker run -ti --privileged --rm forallsecure/tutorial:2.10
ターミナル記録¶
mayhem package
コマンドを使用する¶
Docker を使用できない場合、mayhem package
コマンドを使用すると、ローカルなファイルシステムからアプリケーション バイナリのすべての依存関係を静的に推測し、アプリケーションおよび特定された依存関係を含むミニルート ファイルシステムを構築することで、自動的にアプリケーション バイナリをパッケージ化できます。
Note
ターゲット アプリケーションを適切に実行できるよう、検出されなかった依存関係はユーザーが手動でミニルート ファイルシステムに追加する必要があります。これが Docker を使用してターゲットをパッケージ化することが推奨される理由です。Docker では、ターゲット バイナリはすでに必要な環境と共にバンドルされており、潜在的な依存関係の問題を避けることができます。
mayhem package
コマンドを使用するには、次のパラメーターを指定する必要があります。
mayhem package <path_to_binary> -o <output_path>
Mayhem で非 Docker ターゲットをパッケージ化してテストするための一般的なワークフローは次のとおりです。
mayhem package
コマンドを使用してターゲット バイナリをパッケージ化します。- 生成されたランの Mayhemfile を構成します。
mayhem run
コマンドを使用して Mayhemfile の Mayhem ランを実行します。
Mayhem パッケージの概要がわかったところで、testme
アプリケーションに対して mayhem package
コマンドを実行する方法を確認しましょう。
testme
バイナリをパッケージ化してテストする¶
これまでのレッスンでは、testme
バイナリを Docker コンテナー内のコンテナー アプリケーションとしてパッケージ化してファジングしました。 しかし、Docker を使用できないが testme
バイナリにアクセスする必要がある場合、Mayhem パッケージとして testme
バイナリをパッケージ化してファジングします。
Important
mayhem package
コマンドは Linux OS 環境でのみ動作します。そのため、macOS ユーザーは Linux OS 環境を利用するために forallsecure/tutorial
Docker イメージを使用して以降の手順を行う必要があります。このレッスンは forallsecure/tutorial
Docker イメージでの操作を前提としているため、もともと Linux を使用しているユーザーも同様にすることを推奨します。
docker pull forallsecure/tutorial:2.10
docker run -ti --privileged --rm forallsecure/tutorial:2.10
インターネット接続がなく、forallsecure/tutorial
Docker イメージをプルできない場合、次をダウンロードして展開します: testme.zip
まず、forallsecure/tutorial:2.10
Docker イメージ内の /root/tutorial/testme/v1
に移動します。
1.testme
バイナリをパッケージ化する¶
次に、testme
アプリケーション バイナリをパッケージ化する必要があります。testme
バイナリがあるディレクトリで次のコマンドを実行します。
mayhem package ./testme -o /tmp/testme-pkg
すると、/tmp/testme-pkg
に次のパッケージおよびサブコンテンツが作成されます。
Mayhemfile
: Mayhem ランの構成ファイルtestsuite
: テスト ケース ファイルのリポジトリroot
: アプリケーション バイナリおよび依存関係を含むミニルート ファイルシステム
その後、Mayhemfile を構成してから、パッケージ化されたターゲットに対して Mayhem ランを実行します。
2. Mayhemfile を構成する¶
mayhem package
で生成された Mayhemfile は、Mayhem が mayhem package
から推測したものを使用するよう自動で構成されています。ただし、Mayhem で適切にアプリケーションを実行するには、ユーザーが Mayhemfile
をより詳細に構成する必要がある場合もあります。
Tip
より複雑なアプリケーション用に Mayhemfile
を構成する際は、次の役に立つヒントを心にとめてください。
-
Mayhem は非特権ユーザーとしてターゲットを実行します。ターゲットが特権を必要とする場合、
Mayhemfile
の最上位の構成オプションとしてuid
を設定する必要があります。たとえば、root
としてアプリケーションを実行する必要がある場合、Mayhemfile
でuid: 0
を設定します。 -
デフォルトでは、Mayhem パッケージは Debian "Buster" Docker イメージ内で実行されます。つまり、パッケージは Debian が提供する
libc
およびその他のライブラリを使用します。最上位のディレクティブimage
を使用してベース OS イメージを変更できます。たとえば、アプリケーションが Alpine Linux で実行される場合、image: alpine
を使用します。 -
Mayhem は非常に限定されたユーザー環境でパッケージを実行します。特定の
cmd
の環境変数を設定する場合、リストとしてenv
を設定します。たとえば、アプリケーションの PATH に/usr/local/bin
を追加するには、env: { "PATH": "/usr/local/bin:/usr/bin"}
を設定します。
mayhem package
によって生成される Mayhemfile は次のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
このレッスンでは、Mayhem ランの duration
に 90
を設定します (デフォルトでは、Mayhem ランの継続期間は無期限に設定されます)。上記の例では、testme
バイナリの Mayhem ランは約 90 秒間実行され、バイナリの場所として /root/tutorial/testme/v1/testme
を指していることがわかります。このフォルダーは、mayhem package
が作成する 3 つのアセット (Mayhefile
、testsuite
、および root
) のroot
フォルダーに相当します。この Mayhem ランは、@@
が示すとおり、ファイル入力メソッドを使用して testme
バイナリをテストします。
Info
forallsecure/tutorial
Docker イメージを使用していない場合、自動生成された cmd
ファイルパスは、上記の例と異なる可能性があります。cmd
ファイルパスはターゲット アプリケーションがもともとパッケージ化された場所に基づいて自動生成されます。
Mayhemfile
を適切に構成したら、構成済みの Mayhemfile を含む mayhem package
ディレクトリを指定して mayhem run
コマンドを実行できます。
3. Mayhem ランを実行する¶
最後に、構成済みの Mayhemfile に対して mayhem run
コマンドを実行します。
mayhem run /tmp/testme-pkg
Info
Mayhem ランを正常に実行するには、あらかじめ mayhem login
コマンドを使用して Mayhem サーバーで認証を行う必要があります。詳細については「Mayhem サーバーでの認証」を参照してください。
これで終わりです。90 秒以内に testme
バイナリで不適切な入力検証の欠陥が発見されるはずです。Mayhem UI で結果を参照することができます。
ターゲットを置換してリグレッション テストを実行する¶
これまでは、リグレッション テストを実行する際、Mayhemfile の project
および target
構成値は変えず、cmd
パラメーターを使用して修正/アップデートされた testme
バイナリを指定して Mayhem ランを実行していました。
Note
こうすることで、同じ project
と target
の以前に生成されたテスト ケースを新しいランで効果的に利用し、以前の結果 (クラッシュを起こす) テスト ケースと新しい 変更された testme
バイナリの変更された (修正された) 動作を比較しました。
しかし、Mayhemfile で指定されるターゲット バイナリを更新/修正されたバージョンのアプリケーションに直接置き換えることで、Mayhemfile の構成を変更せずにリグレッション テストを実行することもできます。
更新されたバイナリ アプリケーションのリグレッション テストをセットアップする方法は 2 つあります。
project
およびtarget
Mayhemfile パラメーターを再利用し、cmd
パラメーターが更新/修正されたバイナリの場所を指すよう設定します (説明はこちら) 。- 以前のバイナリを更新されたバージョンに置き換え、同じ Mayhemfile 構成を維持します。
/tmp/testme-pkg
Mayhem パッケージ内の testme
バイナリに直接アクセスすることができるので、直接ターゲットを置換してリグレッション テストをセットアップする方法を確認してみましょう。
Info
Docker ターゲットも非 Docker ターゲットも、両方のリグレッション テスト セットアップ方法を利用できます。ただし、Docker ターゲットの場合、ターゲット バイナリを直接置換するのはより手間がかかります。Docker イメージをインタラクティブ モードで実行し、ターゲット バイナリを置き換え、変更を Docker イメージにコミットし、更新された Docker イメージを Docker Hub または Mayhem Docker レジストリに再度プッシュする必要があります。そのため、リグレッション テスト用にターゲットを置き換える 2 番目の方法は、この非 Docker ターゲットのレッスンで説明されています。
/root/tutorial/testme/v2
ディレクトリには、すでに以前の testme
バイナリを修正されたバージョンに置き換えるための run.sh
スクリプトがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Info
mayhem wait
コマンドに --regression
パラメーターを指定すると、リグレッション テスト フェーズが完了するまで待機します。
/tmp/testme-pkg/root/root/tutorial/testme/v1/testme
の以前にパッケージ化された testme
バイナリが更新された testme
バイナリに置き換えられ、パッケージ化された Mayhemfile に対してリグレッション テストが実行されることがわかります。
/root/tutorial/testme/v2
ディレクトリで次のスクリプトを実行します。
sh run.sh
Mayhem UI で該当ランを参照すると、リグレッション テストの結果で、以前はクラッシュしていたテスト ケースおよび関連する欠陥が修正済みとマークされていることがわかります。
これで終わりです。これで、リグレッション テストをセットアップする 2 種類のワークフローのやり方を学びました。
シード化されたテスト スイートを使用して testme
をテストする¶
Mayhem ランを実行する前に、testsuite
ディレクトリ内の既存のテスト ケースをスキャンして、Mayhem ランのスピードとカバレッジを向上させるためのシードまたは「ジャンプスタート」として利用できます。testsuite
ディレクトリ内の各ファイルは個々の入力テスト ケースおよびその内容を表しています。
Info
指定された既存のテスト ケースがあるテスト スイート フォルダーを Mayhem ランのシードとして使用するよう Mayhemfile を構成する方法の詳細については testsuite パラメーターを参照してください。シード化されたテスト スイートは、Docker ターゲットにも非 Docker ターゲットにも設定できます。
例として、ターゲットの基になる testme
のコードをもう一度見てみましょう。/tmp/testme-pkg
にある Mayhem パッケージに対して、次の内容を含む seed.txt
ファイルを作成して testsuite
フォルダーに置いたとします。
bu
すると、指定された bu
テスト ケースは (aa
などのテストケースと比べて) 最初からより深く testme
バイナリをカバーできるため、シード化されたテスト ケースは Mayhem ランのパフォーマンスを効果的に最適化できるでしょう。
1 2 3 4 5 6 7 8 9 10 |
|
そのため、(内容として bu
を含む) seed.txt
を /tmp/testme-pkg
フォルダーの testsuite
ディレクトリに置きます。
cp seed.txt /tmp/testme-pkg/testsuite
次に、Mayhemfile の target
パラメーターの値を testme-seed
に変更します。こうすると、手動でシード化されたテスト スイートを使用するフレッシュな Mayhem ランが開始されます (デフォルトでは、Mayhem ランは同じ <project>/<target>
の前回のランで生成されたテスト スイートを使用して現行ランをシード化します)。
1 2 3 4 5 6 |
|
ここで mayhem run
コマンドを再実行し、testme
に対してシード化されたテスト スイートを使用します。
$ mayhem run /tmp/testme-pkg
/tmp/tmpo2ifgn0f/testsuite.tgz 100% |#############| Time: 0:00:00 224.7 B/s
Syncing /tmp/testme-pkg/testsuite 100% |####################| Time: 0:00:01
Run started: testme/testme-seed/1
testme/testme-seed/1
testme
バイナリの Mayhem ランは、前回のシード化されていないランに比べて、より速く潜在的な欠陥を発見できたはずです。これは、シード テスト ケース bu
が、不適切な入力検証の欠陥に至る入力 "bug" をチェックする if 文をカバーするのに適したスタート地点を提供したため、Mayhem が 行わなければならないプログラムの探索が少なくなったからです。
⚡ 現実的な演習: testme-npd
バイナリをシード化してテストする¶
バイナリをパッケージ化する方法およびテスト スイートをシード化する方法がわかったので、前のレッスンで使用した testme-npd
に適応できるかやってみましょう。
Note
forallsecure/tutorial
Docker イメージを使用している場合、testme-npd.zip
を Docker コンテナーに移動するには、docker cp
コマンドを使用する必要がある場合があります。詳細については docker cp に関する公式ドキュメントを参照してください。
手順
-
次をダウンロードして展開します: testme-npd
-
mayhem package
コマンドを使用してtestme-npd
バイナリをパッケージ化します。 -
Mayhemfile
のproject
およびtarget
に値testme-npd
を設定します。duration
に60
秒を設定します。 -
testme-npd
パッケージのtestsuite
ディレクトリにseed.txt
テスト ケース ファイルを用意します。 -
mayhem run
コマンドを使用して Mayhem ランを実行します。
Tip
この Mayhem ランは 60 秒しか実行されません。null ポインター間接参照のバグの発見をスピード アップしたいので、どのようなテキストが null ポインター参照のバグをより効率的にカバーできるかを考えてみましょう。
🔍 確認testme-npd
バイナリをシード化してテストする¶
解答
まず、mayhem package
コマンドを使用して testme-npd
バイナリをパッケージ化する必要があります。
mayhem package testme-npd -o /tmp/testme-npd-pkg
Mayhemfile
の project
、target
、および duration
パラメーターに適切な値を指定する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
次に、"nul" を内容とする seed.txt
を作成し、testsuite
ディレクトリに置く必要があります。seed.txt
テスト ケース ファイルが null ポインター間接参照につながる if 文をカバーするのに適したスタート地点を提供するため、null ポインター間接参照のバグがより早く発見されるはずです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
最後に、mayhem run
コマンドを実行すると、次のようなラン ページが表示されます。
mayhem run /tmp/testme-npd-pkg
よくできました! 自力でファズ ターゲットをビルドし、最適化することができました。
✏️ まとめと振り返り¶
このレッスンでは、mayhem package
コマンドを使用してアプリケーションをパッケージ化し、生成された Mayhemfile
を構成し、Mayhem ランを実行し、さらにはテスト スイートをシード化して解析とテストのパフォーマンスを向上させる方法を学びました。
学習内容
1. mayhem package
コマンドを使用してターゲット バイナリをパッケージ化する
-
mayhem package コマンドを使用すると、アプリケーションのすべての依存関係を静的に推測し、アプリケーションおよび特定された依存関係を含むミニルート ファイルシステムを構築することで、自動的にアプリケーション バイナリをパッケージ化できます。
-
mayhem package
コマンドの書式は次のとおりです。mayhem package <path_to_binary> -o <output_path>
-
パッケージの内容は次のようになります。The package contents should look similar to the following:
├── /tmp/testme-pkg └── Mayhemfile - Mayhem ランの構成ファイル └── testsuite - テスト ケース ファイルのリポジトリ └── root - アプリケーション バイナリを含むミニルート ファイルシステム
2. パッケージ化されたターゲットに対して Mayhem ランを実行する
- ターゲットをパッケージ化したら、ターゲット パッケージ内に作成された
Mayhemfile
に対してmayhem run
コマンドを使用できます。
3. ターゲットを置換して (Mayhemfile の変更なしに) リグレッション テストをセット アップし、実行する
-
アップデートされたバイナリ アプリケーションのリグレッション テストをセットアップする方法は 2 つあります。
project
およびtarget
Mayhemfile パラメーターを再利用し、cmd
パラメーターがアップデート/修正されたバイナリを指すよう設定します (説明はこちら)。- 修正されたバイナリで以前のバイナリを置き換え、Mayhemfile 構成をそのまま利用します。
-
directory
/root/tutorial/testme/v2
ディレクトリには、以前のtestme
バイナリを修正されたバージョンに自動的に置き換えるrun.sh
スクリプトがすでに用意されています。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#!/bin/sh # Copy over the new, fixed version into the package. # # Note that we are *overwriting* the old testme application. # Overwriting an old version allows you to use your existing # Mayhemfile cp testme /tmp/testme-pkg/root/root/tutorial/testme/v1/testme # Re-run mayhem. There is no need to edit the Mayhemfile. # The run ID is saved to $id id=$(mayhem run --regression /tmp/testme-pkg) # Wait for the run to finish mayhem wait $id --regression # Sync the test suite to the "testsuite" directory. mayhem sync /tmp/testme-pkg
4. パッケージ化されたターゲットのテスト ケース ファイルをシード化する
-
Mayhem ランを実行する前に、
testsuite
ディレクトリ内の既存のテスト ケースをスキャンして、Mayhem ランのスピードとカバレッジを向上させるためのシードまたは「ジャンプスタート」として利用できます。testsuite
ディレクトリ内の各ファイルは個々の入力テスト ケースおよびその内容を表しています。 -
たとえば、
/tmp/testme-pkg
にある Mayhem パッケージの元になっているtestme
のコードでは、次の内容を含むseed.txt
ファイルを作成してtestsuite
フォルダーに置きます。bu
-
すると、
bu
テスト ケースは (aa
などのテストケースと比べて) 最初からより深くtestme
バイナリをカバーできるため、シード化されたテスト ケースは Mayhem ランのパフォーマンスを効果的に最適化できるでしょう。1 2 3 4 5 6 7 8 9 10
int fuzzme(char *buf) { if(buf[0] == 'b') if(buf[1] == 'u') if(buf[2] == 'g') { printf("You've got it!"); abort(); // Defect: SIGABRT. } return 0; }