ビギナー
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/tutorialDocker イメージを使用して続けることを推奨します。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およびtargetMayhemfile パラメーターを再利用し、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およびtargetMayhemfile パラメーターを再利用し、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; }


