上級
LibFuzzer を使用してインストゥルメントされた C/C++ ターゲット¶

libFuzzer インストゥルメンテーション付きで C/C++ ターゲットをコンパイルする必要がありますか? このレッスンでは、Mayhem で C/C++ libFuzzer ターゲットをコンパイルし、テストする方法を手順を追って説明します。
学習時間の目安: 15 分
このレッスンを終了すると、以下のことができるようになります。
- 不適切な入力検証の欠陥がある C libFuzzer ターゲットをコンパイルし、ファジングする。
- 不適切な入力検証の欠陥がある C++ libFuzzer ターゲットをコンパイルし、ファジングする。
レッスンを駆け足で
始める前に前提条件を確認します。
-
c-libfuzzer.tgz をダウンロードし、
c-libfuzzerDocker イメージをビルドし、指定された Docker レジストリにプッシュします。docker build -t <DOCKERHUB_USERNAME>/c-libfuzzer . docker push <DOCKERHUB_USERNAME>/c-libfuzzerdocker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer . docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer -
Mayhem UI または Mayhem CLI で次の Mayhemfile を使用して
c-libfuzzerDocker イメージに対して Mayhem ランを実行します。1 2 3 4 5 6 7
image: <DOCKERHUB_USERNAME>/c-libfuzzer:latest duration: 90 project: mayhem-examples target: c-libfuzzer cmds: - cmd: /mayhemit libfuzzer: true1 2 3 4 5 6 7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer:latest duration: 90 project: mayhem-examples target: c-libfuzzer cmds: - cmd: /mayhemit libfuzzer: true
以下が必要です。
- Docker がインストールされていること
- 有効なインターネット接続 (Docker Hub ベース イメージをプルするため)
ワン クリック テスト¶
次のボタンをクリックし、Create New Run フローの最後で Start Run をクリック して AFL インストゥルメンテーション付き C ターゲットのテストを開始します。Mayhemfile はすでに用意されているため、何も設定する必要はありません。
Mayhem ランが始まると、次のようなラン ページが表示されます。
すばらしい! Mayhem が libFuzzer を使用してインストゥルメントされた C ターゲットをファジングできることを確認したので、次に、たった今 Mayhem ランを実行した libFuzzer ターゲットをコンパイルし、テストする方法を順を追って説明します。
LibFuzzer インストゥルメンテーション付き C ターゲットのコンパイルとテスト¶
ファイル: c-libfuzzer.tgz
上記の c-libfuzzer.tgz をダウンロードして展開し、次の mayhemit.c のソース コードを見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
すぐに、ソース コードにいくつか追加のヘッダー ファイル (stddef.h および stdint.h ) があることに気づくでしょう。
さらに、ソース コードは通常の main 関数ではなく、LLVMFuzzerTestOneInput 関数を使用してバイト配列を受け取り、ターゲット関数 mayhemit のファジングに使用しています。入力が "bug" の場合、プログラムは不適切な入力検証のエラーでクラッシュします。
次に、関連する Dockerfile を調べます。
1 2 3 4 5 6 7 | |
- 行 1: libFuzzer 依存関係にアクセスするため、
fuzzers/libfuzzer:12.0ベース イメージがインポートされています。 - 行 2: Docker コンテナーに
mayhemit.cソース コードがコピーされています。 - 行 3:
fsanitizeパラメーターを指定してclangコンパイラを使用し、mayhemit実行ファイルをコンパイルして libFuzzer ライブラリにリンクしています。 - 行 7: Docker コンテナーのデフォルト実行ファイルとして
/mayhemit実行ファイルが設定されています。
Info
clang および -fsanitize の使用に関する詳細については、libFuzzer usage の公式ドキュメントを参照してください。
次に、docker build および docker push コマンドを使用して、Docker イメージをビルドして Docker Hub レジストリにプッシュする必要があります。
次に、docker build および docker push コマンドを使用して、Docker イメージをビルドして Mayhem サーバーにプッシュする必要があります。$MAYHEM_DOCKER_REGISTRY は、プライベートな Mayhem Docker レジストリの URL を表します。
docker build -t <DOCKERHUB_USERNAME>/c-libfuzzer .
docker push <DOCKERHUB_USERNAME>/c-libfuzzer
docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer
情報
mayhem login コマンドを使用して内部的な Mayhem Docker レジストリの URL を検索し、次のコマンドを使用して DOCKER_REGISTRY 環境変数を設定できます:
export DOCKER_REGISTRY=tutorial.forallsecure.com:5000
DOCKER_REGISTRY 環境変数に自身の Mayhem Docker レジストリ URL を設定する必要があります。
新しく作成した Docker イメージをパブリックな Docker Hub レジストリに正常にプッシュしたら、Mayhem UI から新規ランを作成し、<DOCKERHUB_USERNAME>/c-libfuzzer Docker イメージを検索します。Mayhemfile が次のようになっていることを確認します。
新しく作成した Docker イメージをプライベートな Mayhem Docker レジストリに正常にプッシュしたら、Mayhem UI から新規ランを作成し、forallsecure/c-libfuzzer Docker イメージを検索します。Mayhemfile が次のようになっていることを確認します。
1 2 3 4 5 6 7 | |
1 2 3 4 5 6 7 | |
新規ランの作成フローの最後に到達するまで [Next] をクリックし、[Start Run] をクリックして Mayhem ランを実行します。次のようなラン ページが表示されます。
おめでとうございます! Mayhem で C libFuzzer ターゲットのファジングが成功しました。
⚡ 現実的な演習: LibFuzzer インストゥルメンテーション付き C++ ターゲットのコンパイルとテスト¶
libFuzzer インストゥルメンテーション付き C ターゲットのコンパイルおよびテスト手順がわかったので、次に、同じことを境界外欠陥がある libFuzzer インストゥルメンテーション付き C++ ターゲットに対して行うことができるか、やってみましょう。
ファイル: mayhemit-out-of-bounds-unsolved.zip
手順
mayhemit.cソース コードを変更し、インデックス境界外欠陥を追加します。
1 2 3 4 5 6 7 8 9 10 11 | |
docker buildコマンドを使用してDockerfileを再ビルドし、結果の Docker イメージを<DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-boundsとしてタグ付けします。docker pushコマンドを使用してパブリックな Docker Hub レジストリに<DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-boundsDocker イメージをプッシュします。- Mayhem UI または Mayhem CLI を使用して
<DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-boundsDocker イメージをファジングします。Mayhemfile が適切に設定されていることを確認します。
docker buildコマンドを使用してDockerfileを再ビルドし、結果の Docker イメージをcpp-libfuzzer-mayhemit-out-of-boundsとしてタグ付けします。docker pushコマンドを使用してプライベートな Mayhem Docker レジストリにcpp-libfuzzer-mayhemit-out-of-boundsDocker イメージをプッシュします。- Mayhem UI または Mayhem CLI を使用して
cpp-libfuzzer-mayhemit-out-of-boundsDocker イメージをテストします。Mayhemfile が適切に設定されていることを確認します。
🔍 確認LibFuzzer C++ ターゲットのコンパイルとテスト¶
解答
模範解答: mayhemit-out-of-bounds-solved.zip
結果を確認しましょう。まず、cpp-libfuzzer-mayhemit-out-of-bounds ターゲットのソース コードを見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
c-libfuzzer ターゲットのソース コードと比較したとき、extern "C" キーワードが使用されているという違いがあることに気づいたかもしれません。extern "C" キーワードは、C++ の関数名に C リンケージを持たせ、クライアントの C コードが関数の宣言だけを含む C 互換ヘッダー ファイルを使用して関数にリンク (使用) できるようにします。
いっぽう、cpp-libfuzzer-mayhemit-out-of-bounds ターゲットに対応する Dockerfile には、バイナリをコンパイルするには、clang++ C++ コンパイラと fsanitize パラメーターを組み合わせて mayhemit 実行ファイルをコンパイルし、libFuzzer ライブラリにリンクするよう指定されています。
1 2 3 4 5 6 7 | |
次に、結果の cpp-libfuzzer-mayhemit-out-of-bounds Docker イメージをビルドし、タグを付け、Docker Hub レジストリにプッシュする必要がああります。
次に、結果の cpp-libfuzzer-mayhemit-out-of-bounds Docker イメージをビルドし、タグを付け、Mayhem Docker レジストリにプッシュする必要がああります。
docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds .
docker push <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/cpp-libfuzzer-mayhemit-out-of-bounds .
docker push $MAYHEM_DOCKER_REGISTRY/cpp-libfuzzer-mayhemit-out-of-bounds
別の方法として、付属の Makefil を使用し、MAYHEM_DOCKER_REGISTRY` 環境変数を設定して次のコマンドを実行することで、簡単に結果の Docker イメージをビルドし、プッシュすることもできます。
make build
make push
最後に、Mayhem UI または Mayhem CLI を使用して、アップロードされた <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds Docker イメージに対して Mayhem ランを実行します。Mayhemfile は次のようになっているはずです。
最後に、Mayhem UI または Mayhem CLI を使用して、アップロードされた cpp-libfuzzer-mayhemit-out-of-bounds Docker イメージに対して Mayhem ランを実行します。Mayhemfile は次のようになっているはずです。
1 2 3 4 5 6 7 | |
1 2 3 4 5 6 7 | |
最終的なラン ページは次のように表示されるはずです。
おめでとうございます! Mayhem がインデックス境界外の欠陥を発見しました。スクラッチから libFuzzer C++ ターゲットをビルドし、Mayhem を使用してバグを検出できました。
✏️ まとめと振り返り¶
このレッスンでは、Mayhem で libFuzzer を使用してインストゥルメントされた C/C++ ターゲットをコンパイルし、テストする方法を学びました。
学習内容
1. 不適切な入力検証の欠陥がある C libFuzzer ターゲットをコンパイルし、テストする。
-
ソース コードには次の欠陥が含まれているはずです:
1 2 3 4 5 6 7 8 9 10 11
int mayhemit(char *buf, unsigned len) { if (len >= 3) if(buf[0] == 'b') if(buf[1] == 'u') if(buf[2] == 'g') { printf("You've got it!"); abort(); // Defect: SIGABRT. } return 0; } -
C libFuzzer ターゲットをファジングするには、次の
DockerfileおよびMayhemfileを使用して C プログラムを含む Docker イメージをビルドし、Mayhem でファジングを実行します。1 2 3 4 5 6 7
FROM fuzzers/libfuzzer:12.0 COPY mayhemit.c . RUN clang-12 -fsanitize=fuzzer,address mayhemit.c -o /mayhemit # Set to fuzz! ENTRYPOINT [] CMD /mayhemit1 2 3 4 5 6 7
image: <DOCKERHUB_USERNAME>/c-libfuzzer:latest duration: 90 project: mayhem-examples target: c-libfuzzer cmds: - cmd: /mayhemit libfuzzer: true1 2 3 4 5 6 7
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-libfuzzer:latest duration: 90 project: mayhem-examples target: c-libfuzzer cmds: - cmd: /mayhemit libfuzzer: true
2. 境界外の欠陥がある C++ libFuzzer ターゲットをコンパイルし、ファジングする。
-
ソース コードには次の欠陥が含まれているはずです:
1 2 3 4 5 6 7 8 9 10 11
int mayhemit(char *buf, unsigned len) { if (len >= 3 && len < 5) if(buf[0] == 'b') if(buf[1] == 'u') if(buf[2] == 'g') { printf("You've got it!"); return buf[10]; } return 0; } -
C++ libFuzzer ターゲットをファジングするには、次の
DockerfileおよびMayhemfileを使用して C++ プログラムを含む Docker イメージをビルドし、Mayhem でファジングを実行します。1 2 3 4 5 6 7
FROM fuzzers/libfuzzer:12.0 COPY mayhemit.cpp . RUN clang++-12 -fsanitize=fuzzer,address -fno-inline mayhemit.cpp -o /mayhemit # Set to fuzz! ENTRYPOINT [] CMD /mayhemit1 2 3 4 5 6 7
image: <DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds:latest duration: 90 project: mayhem-examples target: cpp-libfuzzer-mayhemit-out-of-bounds cmds: - cmd: /mayhemit libfuzzer: true1 2 3 4 5 6 7
image: $MAYHEM_DOCKER_REGISTRY/cpp-libfuzzer-mayhemit-out-of-bounds:latest duration: 90 project: mayhem-examples target: mayhemit-out-of-bounds cmds: - cmd: /mayhemit libfuzzer: true

