コンテンツにスキップ

上級

ベース実行ファイル C/C++ ターゲット

c-cpp-logo

テスト対象はインストゥルメンテーションされていない、またはベース実行ファイルの C/C++ ターゲットでしょうか? このレッスンでは、Mayhem でベース実行ファイル C/C++ ターゲットをテストする方法を手順を追って説明します。

学習時間の目安: 15 分

このレッスンを終了すると、以下のことができるようになります。

  1. 不適切な入力検証の欠陥があるベース実行ファイル C ターゲットをコンパイルし、ファジングする。
  2. 不適切な入力検証の欠陥があるベース実行ファイル C++ ターゲットをコンパイルし、ファジングする。

レッスンを駆け足で

始める前に前提条件を確認します。

  1. c-base-executable.tgz をダウンロードし、c-base-executable` Docker イメージをビルドし、指定された Docker レジストリにプッシュします。

    docker build -t <DOCKERHUB_USERNAME>/c-base-executable .
    docker push <DOCKERHUB_USERNAME>/c-base-executable
    
    docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable .
    docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable
    
  2. Mayhem UI または Mayhem CLI で次の Mayhemfile を使用して c-base-executable Docker イメージに対して Mayhem ランを実行します。

    1
    2
    3
    4
    5
    6
    image: <DOCKERHUB_USERNAME>/c-base-executable:latest
    duration: 90
    project: mayhem-examples
    target: c-base-executable
    cmds:
      - cmd: /mayhemit @@
    
    1
    2
    3
    4
    5
    6
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable:latest
    duration: 90
    project: mayhem-examples
    target: c-base-executable
    cmds:
      - cmd: /mayhemit @@
    

以下が必要です。

  • Docker がインストールされていること *有効なインターネット接続 (Docker Hub ベース イメージをプルするため)

ワン クリック テスト

次のボタンをクリックし、Create New Run フローの最後で Start Run をクリックしてベース実行ファイル C ターゲットのテストを開始します。Mayhemfile はすでに用意されているため、何も設定する必要はありません。

Mayhem ランが始まると、次のようなラン ページが表示されます。

c-base-executable-run

すばらしい! Mayhem によるベース実行ファイル C ターゲットのテストを確認したので、次に、たった今実行した Mayhem ランの c-base-executable ターゲットをコンパイルし、テストする方法を順を追って説明します。

ベース実行ファイル C ターゲットのコンパイルとテスト

ファイル: c-base-executable.tgz

上記の c-base-executable.tgz をダウンロードして展開し、次のバグのある mayhemit.c プログラムを見てみましょう。

 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
33
34
35
36
#include <stdio.h>
#include <string.h>

int mayhemit(char *buf)
{
  if(strlen(buf) >= 3)
    if(buf[0] == 'b')
      if(buf[1] == 'u')
        if(buf[2] == 'g') {
          printf("You've got it!");
          abort(); // Defect: SIGABRT.
        }
  return 0;
}

int main(int argc, char *argv[])
{
  FILE *f;
  char buf[12];

  if(argc != 2){
    fprintf(stderr, "Must supply a text file\n");
    return -1;
  }
  f = fopen(argv[1], "r");
  if(f == NULL){
    fprintf(stderr, "Could not open %s\n", argv[1]);
    return -1;
  }
  if(fgets(buf, sizeof(buf), f) == NULL){
    fprintf(stderr, "Could not read from %s\n", argv[1]);
    return -1;
  }
  mayhemit(buf);
  return 0;
}

プログラムの行 1-2 でヘッダー ファイル stdio.h および string.h がインクルードされ、次の 2 つの注目するべき関数があるのがわかります: main および mayhemit

Info

C++ プログラムは iostream ヘッダー ファイルを使用しますが、C プログラムは stdio.h ヘッダー ファイルを使用します。他の C++ プログラムを参照すると、通常、ソース コードは iostream ヘッダー ファイルを使用しています。

main 関数はファイル入力を処理し、プログラムに適切にファイルが指定されている場合、mayhemit 関数を呼び出します。その後、mayhemit 関数は入力ファイルを解析し、プログラムに入力されたテスト ケースが "bug" である場合、不適切な入力検証エラーを返します。そのため、このターゲットで不適切な入力検証の欠陥を明らかにするには、Mayhem はプログラムをファジングしてクラッシュを起こすテスト ケース "bug" を入力できなければなりません。

しかしその前に、プログラムをファジングするには、プログラムをコンパイルし、ターゲットを Mayhem にアップロードして取り込めるようにする必要があります。この最初のステップとして、ターゲット アプリケーションとプログラム依存性を組み合わせた Docker コンテナーを使用し、Mayhem が正常に C ターゲットを実行してファジングできるようにすることを推奨します。

関連する Dockerfile を見てみると、次の操作があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM debian:buster-slim as builder
RUN apt-get update && \
    apt-get install -y gcc make libc6-dbg && \
    rm -rf /var/lib/apt/lists/*
COPY mayhemit.c .
RUN gcc -o mayhemit -Wno-div-by-zero -fno-stack-protector -zexecstack -no-pie mayhemit.c

# Set to fuzz!
ENTRYPOINT []
CMD /mayhemit @@
  • 行 1: 新規 Docker コンテナーのベース イメージとして debian:buster-slim イメージがインポートされています。
  • 行 2-4: 必要な依存関係がダウンロードされ、新規 Docker コンテナーにインストールされています。
  • 行 5: Docker コンテナーに mayhemit.c ソース コードがコピーされています。
  • 行 6: gcc C コンパイラを使用して mayhemit.c ソース コードが mayhemit 実行ファイルにコンパイルされています。
  • 行 10: Docker コンテナーのデフォルト実行ファイルとして /mayhemit @@ コマンドが設定されています。

次に、docker build および docker push コマンドを使用して、実際に Docker イメージをビルドして Docker Hub レジストリにプッシュする必要があります。

次に、docker build および docker push コマンドを使用して、実際に Docker イメージをビルドして Mayhem サーバーにプッシュする必要があります。$MAYHEM_DOCKER_REGISTRY は、プライベートな Mayhem Docker レジストリの URL の環境変数を表します。

情報

たとえば、次のコマンドを実行して、$DOCKER_REGISTRY 環境変数に tutorial.forallsecure.com:5000 を設定できます。

export DOCKER_REGISTRY=tutorial.forallsecure.com:5000

c-base-executable ディレクトリで次のコマンドを実行します。

docker build -t <DOCKERHUB_USERNAME>/c-base-executable .
docker push <DOCKERHUB_USERNAME>/c-base-executable
docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable

新しく作成した Docker イメージをパブリックな Docker Hub レジストリに正常にプッシュしたら、Mayhem UI から新規ランを作成し、アップロードされた <DOCKERHUB_USERNAME>/c-base-executable Docker イメージを選択します。新規ランの作成フローで [Next] をクリックし、Mayhem ファイルが次のようになっていることを確認してからランを開始します。

新しく作成した Docker イメージをプライベートな Mayhem Docker レジストリに正常にプッシュしたら、Mayhem UI から新規ランを作成し、アップロードされた forallsecure/c-base-executable Docker イメージを選択します。新規ランの作成フローで [Next] をクリックし、Mayhem ファイルが次のようになっていることを確認してからランを開始します。

1
2
3
4
5
6
image: <DOCKERHUB_USERNAME>/c-base-executable:latest
duration: 90
project: mayhem-examples
target: c-base-executable
cmds:
  - cmd: /mayhemit @@
1
2
3
4
5
6
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable:latest
duration: 90
project: mayhem-examples
target: c-base-executable
cmds:
  - cmd: /mayhemit @@

Mayhem が不適切な入力検証の欠陥を発見できたことがわかるはずです。C ターゲットのコンパイルとテストが成功しました。

c-base-executable-run

現実的な演習: ベース実行ファイル C++ ターゲットのコンパイルとテスト

ベース実行ファイル C ターゲットのコンパイルおよびテスト手順がわかったので、次に、同じことをベース実行ファイル C++ ターゲットに対して行うことができるか、やってみましょう。

ファイル: cpp-base-executable.tgz

手順

  • 上記の cpp-base-executable.tgz ファイルをダウンロードして展開し、Mayhem が <DOCKERHUB_USERNAME>/cpp-base-executable ターゲットをファジングできるよう、docker build および docker push コマンドを使用して Docker イメージをビルドしてプッシュします。
  • Mayhem UI から、新しくアップロードした <DOCKERHUB_USERNAME>/cpp-base-executable Docker イメージをファジングします。
  • 上記の cpp-base-executable.tgz ファイルをダウンロードして展開し、Mayhem が forallsecure/cpp-base-executable ターゲットをファジングできるよう、docker build および docker push コマンドを使用して Docker イメージをビルドしてプッシュします。
  • Mayhem UI から、新しくアップロードした forallsecure/cpp-base-executable Docker イメージをテストします。

🔍 確認ベース実行ファイル C++ ターゲットのコンパイルとテスト

解答

結果を確認しましょう。まず、mayhemit.cpp プログラムのソース コードを見てみましょう。

 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
33
34
35
36
#include <iostream>
#include <string.h>

int mayhemit(char *buf)
{
  if(strlen(buf) >= 3)
    if(buf[0] == 'b')
      if(buf[1] == 'u')
        if(buf[2] == 'g') {
          printf("You've got it!");
          abort(); // Defect: SIGABRT.
        }
  return 0;
}

int main(int argc, char *argv[])
{
  FILE *f;
  char buf[12];

  if(argc != 2){
    fprintf(stderr, "Must supply a text file\n");
    return -1;
  }
  f = fopen(argv[1], "r");
  if(f == NULL){
    fprintf(stderr, "Could not open %s\n", argv[1]);
    return -1;
  }
  if(fgets(buf, sizeof(buf), f) == NULL){
    fprintf(stderr, "Could not read from %s\n", argv[1]);
    return -1;
  }
  mayhemit(buf);
  return 0;
}

C の stdio.h ヘッダーファイルが C++ の iostream ヘッダーに置き換えられている以外は、cpp-base-executable ソース コードは、ほとんど c-base-executable ソース コードと同じであることがわかります。

次に、対応する Dockerfile を確認します。次の操作があるのがわかります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM debian:buster-slim as builder
RUN apt-get update && \
    apt-get install -y gcc g++ make libc6-dbg && \
    rm -rf /var/lib/apt/lists/*
COPY mayhemit.cpp .
RUN g++ -Wno-div-by-zero -fno-stack-protector -zexecstack -no-pie -o mayhemit mayhemit.cpp

# Set to fuzz!
ENTRYPOINT []
CMD /mayhemit @@
  • 行 1: 新しい Docker コンテナーのベース イメージとして、debian:buster-slim イメージがインポートされています。
  • 行 2-4: 必要な依存関係がダウンロードされ、新しい Docker イメージ内にインストールされています。
  • 行 5: mayhemit.cpp ソース コードが Docker コンテナーにコピーされています。
  • 行 6: g++ C++ コンパイラを使用して mayhemit.cpp ソース コードが mayhemit 実行ファイルにコンパイルされています。
  • 行 10: 結果の Docker コンテナーのデフォルト実行ファイルとして /mayhemit @@ コマンドが設定されています。

これで、ほとんど終わりです。次は、前にも行ったように、Docker イメージをビルドしてプッシュしてから、コンテナー化された cpp-base-executable ターゲットを Mayhem で実行します。

docker build -t <DOCKERHUB_USERNAME>/cpp-base-executable .
docker push <DOCKERHUB_USERNAME>/cpp-base-executable
docker build -t $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-base-executable .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-base-executable

ランの Mayhemfile は次のようになっているはずです。

1
2
3
4
5
6
image: <DOCKERHUB_USERNAME>/cpp-base-executable:latest
duration: 90
project: mayhem-examples
target: cpp-base-executable
cmds:
  - cmd: /mayhemit @@
1
2
3
4
5
6
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-base-executable:latest
duration: 90
project: mayhem-examples
target: cpp-base-executable
cmds:
  - cmd: /mayhemit @@

最後に、次のようなラン ページが表示されるはずです。

cpp-base-executable-run

cpp-base-executable ターゲットをテストできた場合、よくできました! 自力で C++ ターゲットをビルドしてテストできました。

✏️ まとめと振り返り

このレッスンでは、Mayhem でベース実行ファイル C/C++ ターゲットをコンパイルし、テストする方法を学びました。


学習内容

1. 不適切な入力検証の欠陥があるベース実行ファイル C ターゲットをコンパイルし、テストする。
  • ソース コードには次の欠陥が含まれているはずです:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    int mayhemit(char *buf)
    {
      if(strlen(buf) >= 3)
        if(buf[0] == 'b')
          if(buf[1] == 'u')
            if(buf[2] == 'g') {
              printf("You've got it!");
              abort(); // Defect: SIGABRT.
            }
      return 0;
    }
    

  • C のベース実行ファイル ターゲットをファジングするには、次の Dockerfile および Mayhemfile を使用して C プログラムを含む Docker イメージをビルドし、Mayhem でファジングを実行します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    FROM debian:buster-slim as builder
    RUN apt-get update && \
        apt-get install -y gcc make libc6-dbg && \
        rm -rf /var/lib/apt/lists/*
    COPY mayhemit.c .
    RUN gcc -o mayhemit -Wno-div-by-zero -fno-stack-protector -zexecstack -no-pie mayhemit.c
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD /mayhemit @@
    

    1
    2
    3
    4
    5
    6
    image: <DOCKERHUB_USERNAME>/c-base-executable:latest
    duration: 90
    project: mayhem-examples
    target: c-base-executable
    cmds:
      - cmd: /mayhemit @@
    
    1
    2
    3
    4
    5
    6
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/c-base-executable:latest
    duration: 90
    project: mayhem-examples
    target: c-base-executable
    cmds:
      - cmd: /mayhemit @@
    
2. 不適切な入力検証の欠陥があるベース実行ファイル C++ ターゲットをコンパイルし、ファジングする。
  • ソース コードには次の欠陥が含まれているはずです:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    int mayhemit(char *buf)
    {
      if(strlen(buf) >= 3)
        if(buf[0] == 'b')
          if(buf[1] == 'u')
            if(buf[2] == 'g') {
              printf("You've got it!");
              abort(); // Defect: SIGABRT.
            }
      return 0;
    }
    

  • C++ のベース実行ファイル ターゲットをファジングするには、次の Dockerfile および Mayhemfile を使用して C++ プログラムを含む Docker イメージをビルドし、Mayhem でファジングを実行します。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    FROM debian:buster-slim as builder
    RUN apt-get update && \
        apt-get install -y gcc g++ make libc6-dbg && \
        rm -rf /var/lib/apt/lists/*
    COPY mayhemit.cpp .
    RUN g++ -Wno-div-by-zero -fno-stack-protector -zexecstack -no-pie -o mayhemit mayhemit.cpp
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD /mayhemit @@
    

    1
    2
    3
    4
    5
    6
    image: <DOCKERHUB_USERNAME>/cpp-base-executable:latest
    duration: 90
    project: mayhem-examples
    target: cpp-base-executable
    cmds:
      - cmd: /mayhemit @@
    
    1
    2
    3
    4
    5
    6
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/cpp-base-executable:latest
    duration: 90
    project: mayhem-examples
    target: cpp-base-executable
    cmds:
      - cmd: /mayhemit @@