上級
LibFuzzer を使用してインストゥルメントされた C/C++ ターゲット¶
libFuzzer インストゥルメンテーション付きで C/C++ ターゲットをコンパイルする必要がありますか? このレッスンでは、Mayhem で C/C++ libFuzzer ターゲットをコンパイルし、テストする方法を手順を追って説明します。
学習時間の目安: 15 分
このレッスンを終了すると、以下のことができるようになります。
- 不適切な入力検証の欠陥がある C libFuzzer ターゲットをコンパイルし、ファジングする。
- 不適切な入力検証の欠陥がある C++ libFuzzer ターゲットをコンパイルし、ファジングする。
レッスンを駆け足で
始める前に前提条件を確認します。
-
c-libfuzzer.tgz をダウンロードし、
c-libfuzzer
Docker イメージをビルドし、指定された Docker レジストリにプッシュします。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 UI または Mayhem CLI で次の Mayhemfile を使用して
c-libfuzzer
Docker イメージに対して 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: true
1 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-bounds
Docker イメージをプッシュします。- Mayhem UI または Mayhem CLI を使用して
<DOCKERHUB_USERNAME>/cpp-libfuzzer-mayhemit-out-of-bounds
Docker イメージをファジングします。Mayhemfile が適切に設定されていることを確認します。
docker build
コマンドを使用してDockerfile
を再ビルドし、結果の Docker イメージをcpp-libfuzzer-mayhemit-out-of-bounds
としてタグ付けします。docker push
コマンドを使用してプライベートな Mayhem Docker レジストリにcpp-libfuzzer-mayhemit-out-of-bounds
Docker イメージをプッシュします。- Mayhem UI または Mayhem CLI を使用して
cpp-libfuzzer-mayhemit-out-of-bounds
Docker イメージをテストします。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 /mayhemit
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: true
1 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 /mayhemit
1 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: true
1 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