コンテンツにスキップ

上級

Go-Fuzz を使用した LibFuzzer インストゥルメンテーション付き Go ターゲット

go-logo google-logo

LibFuzzer インストゥルメンテーション付き Go ターゲットが必要ですか? このレッスンでは、Go-Fuzz を使用して LibFuzzer インストゥルメンテーション付き Go ターゲットをコンパイルする方法を順を追って説明します。

学習時間の目安: 15 分

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

  1. 到達可能なアサーションの欠陥がある go-fuzz を使用した libFuzzer インストゥルメンテーション付き Go ターゲットをコンパイルし、ファジングする。
  2. インデックス境界外の欠陥がある go-fuzz を使用した libFuzzer インストゥルメンテーション付き Go ターゲットをコンパイルし、ファジングする。

レッスンを駆け足で

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

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

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

    1
    2
    3
    4
    5
    image: <DOCKERHUB_USERNAME>/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    
    1
    2
    3
    4
    5
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    

以下が必要です。

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

ワン クリック テスト

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

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

go-go-fuzz-run

すばらしい! Mayhem による libFuzzer を使用してインストゥルメントされた Go ターゲットのテストを確認したので、次に、たった今実行した Mayhem ランの go-go-fuzz ターゲットをコンパイルし、ファジングする方法を順を追って説明します。

Go-Fuzz を使用した LibFuzzer インストゥルメンテーション付き Go ターゲットのコンパイルとテスト

ファイル: go-go-fuzz.tgz

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package test

func mayhemit(bytes []byte) int {
    content := string(bytes)
    if len(content) >= 3 {
        if string(content[0]) == "b" {
            if string(content[1]) == "u" {
                if string(content[2]) == "g" {
                    panic("found a bug!")
                }
            }
        }
    }
    return 0
}

func Fuzz(data []byte) int {
    _ = mayhemit(data)
    return 0
}

注目するべき 2 つの関数 Fuzz および mayhemit があるのがわかります。Fuzz 関数は go-fuzz ファザーへのエントリポイントとして機能し、行 18mayhemit 関数を呼び出して入力テスト ケースが "bug" であるかをチェックします。テスト ケースが "bug" である場合、panic() 関数の行 9 で発生する到達可能なアサーション エラーによってプログラムがクラッシュします。

このプログラムがどのようにコンパイルされるかを見てみましょう。

1
2
3
4
5
6
7
8
FROM fuzzers/go-fuzz:1.2.0
COPY mayhemit.go .
RUN go-fuzz-build -libfuzzer -o mayhemit.a && \
    clang -fsanitize=fuzzer mayhemit.a -o mayhemit.libfuzzer

# Set to fuzz!
ENTRYPOINT []
CMD ["/go/mayhemit.libfuzzer"]
  • 行 1: go-fuzz およびその依存関係へのアクセスを提供する Docker コンテナーのベース環境として fuzzers/go-fuzz:1.2.0 ベース イメージが設定されています。
  • 行 2: Docker コンテナーに mayhemit.go ソース コードがコピーされています。
  • 行 3: go-fuzz-build コンパイラを使用して mayhemit ソース コードが実行ファイルにコンパイルされています。このケースでは、go-fuzz-build コンパイラは libFuzzer と共に使用可能な libFuzzer アーカイブを出力します。
  • 行 8: ビルドされた Docker イメージのデフォルト実行ファイルとして /go/mayhemit.libfuzzer 実行ファイルが設定されています。

次に、go-go-fuzz フォルダーから次の docker コマンドを実行して <DOCKERHUB_USERNAME>/go-go-fuzz Docker イメージをビルドし、Docker Hub レジストリにプッシュします。

次に、go-go-fuzz フォルダーから次の docker コマンドを実行して forallsecure/go-go-fuzz Docker イメージをビルドし、Mayhem Docker レジストリにプッシュします。

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/go-go-fuzz .
docker push <DOCKERHUB_USERNAME>/go-go-fuzz
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz .
docker push $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz

情報

mayhem login コマンドを使用して内部的な Mayhem Docker レジストリの URL を検索し、次のコマンドを使用して DOCKER_REGISTRY 環境変数を設定できます:

export DOCKER_REGISTRY=tutorial.forallsecure.com:5000
ここでは、サンプルの Docker レジストリ URL を設定していますが、DOCKER_REGISTRY 環境変数に自身の Mayhem Docker レジストリ URL を設定する必要があります。

Docker イメージが正常にプッシュされたら、Mayhem UI に移動し、新規 Mayhem ランのソースとして Docker Hub を選択します。先ほどアップロードした <DOCKERHUB_USERNAME>/go-go-fuzz Docker イメージを検索し、Mayhemfile が次のようになっていることを確認します。

Docker イメージが正常にプッシュされたら、Mayhem UI に移動し、新規 Mayhem ランのソースとして Mayhem Docker Registry を選択します。先ほどアップロードした forallsecure/go-go-fuzz Docker イメージを検索し、Mayhemfile が次のようになっていることを確認します。

1
2
3
4
5
image: <DOCKERHUB_USERNAME>/go-go-fuzz:latest
project: mayhem-examples
target: go-go-fuzz
cmds:
  - cmd: /go/mayhemit.libfuzzer
1
2
3
4
5
image: $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz:latest
project: mayhem-examples
target: go-go-fuzz
cmds:
  - cmd: /go/mayhemit.libfuzzer

次に、新規ランの作成フローの最後に到達するまで [Next] をクリックし、[Click Start] ボタンをクリックして Go ターゲットのテストを開始します。次のようなラン ページが表示されます。

go-go-fuzz-run

おめでとうございます! go-fuzz インストゥルメンテーション付き Go ターゲットのファジングが成功しました。

現実的な演習: Go-Fuzz を使用した mayhemit-out-of-bounds LibFuzzer Go ターゲットのビルドとテスト

go-fuzz を使用した libFuzzer インストゥルメンテーション付き Go ターゲットのビルドおよびテスト方法がわかったところで、ソース コードを修正して到達可能なアサーションの欠陥ではなくインデックス境界外の欠陥を検出できるかどうかやってみましょう。

ファイル: mayhemit-out-of-bounds-unsolved.zip

手順

  • mayhemit.go ソース コードを変更し、配列境界外欠陥を追加します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func mayhemit(bytes []byte) int {
    content := string(bytes)
    if len(content) >= 3 && len(content) < 5 {
        if string(content[0]) == "b" {
            if string(content[1]) == "u" {
                if string(content[2]) == "g" {
                    var x = content[10]
                    fmt.Println(x)
                }
            }
        }
    }
    return 0
}
  • docker build コマンドを使用して Dockerfile を再ビルドし、結果の Docker イメージを <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds としてタグ付けします。
  • docker push コマンドを使用してパブリックな Docker Hub レジストリに <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds Docker イメージをプッシュします。
  • Mayhem UI または Mayhem CLI を使用して <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds Docker イメージをファジングします。Mayhemfile が適切に設定されていることを確認します。
  • docker build コマンドを使用して Dockerfile を再ビルドし、結果の Docker イメージを $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds としてタグ付けします。
  • docker push コマンドを使用してプライベートな Mayhem Docker レジストリに $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds Docker イメージをプッシュします。
  • Mayhem UI または Mayhem CLI を使用して $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds Docker イメージをテストします。Mayhemfile が適切に設定されていることを確認します。

🔍 確認Go-Fuzz を使用した mayhemit-out-of-bounds LibFuzzer Go ターゲットのビルドとテスト

解答

模範解答: mayhemit-out-of-bounds-solved.zip

まず、mayhemit 関数をファジングしたとき、入力テスト ケース "bug" がインデックス境界外エラーを発生させるよう、最大長の制約 len(content) < 5 および誤った呼び出し content[10] を追加します。

 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
package test

import (
    "fmt"
)

func mayhemit(bytes []byte) int {
    content := string(bytes)
    if len(content) >= 3 && len(content) < 5 {
        if string(content[0]) == "b" {
            if string(content[1]) == "u" {
                if string(content[2]) == "g" {
                    var x = content[10]
                    fmt.Println(x)
                }
            }
        }
    }
    return 0
}

func Fuzz(data []byte) int {
    _ = mayhemit(data)
    return 0
}

次に、Dockerfile と同じディレクトリで docker build コマンドを実行し、結果の Docker イメージに <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds というタグを付けます。

次に、Dockerfile と同じディレクトリで docker build コマンドを実行し、結果の Docker イメージに $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds` というタグを付けます。

docker build -f Dockerfile -t <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds .
docker build -f Dockerfile -t $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds .

次に、<DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds Docker イメージにタグを付けてパブリックな Docker Hub レジストリにプッシュします。

次に、$MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds Docker イメージにタグを付けてプライベートな Mayhem Docker レジストリにプッシュします。

docker push <DOCKERHUB_USERNAME>/tutorial/go-go-fuzz-mayhemit-out-of-bounds
docker push $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds

Note

この例では、go-go-fuzz-mayhemit-out-of-bounds Docker イメージを tutorial.forallsecure.com Mayhem デプロイメントのポート 5000 にあるプライベートな Mayhem Docker レジストリにアップロードしています。Docker イメージを正常にアップロードするには、URL を自身の Docker レジストリに変更する必要があります。

別の方法として、付属の Makefil を使用し、MAYHEM_DOCKER_REGISTRY` 環境変数を設定して次のコマンドを実行することで、簡単に結果の Docker イメージをビルドし、プッシュすることもできます。

make build
make push

最後に、Mayhem UI または Mayhem CLI を使用して、アップロードされた <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds Docker イメージに対して Mayhem ランを実行します。Mayhemfile は次のようになっているはずです。

最後に、Mayhem UI または Mayhem CLI を使用して、アップロードされた $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds Docker イメージに対して Mayhem ランを実行します。Mayhemfile は次のようになっているはずです。

1
2
3
4
5
6
7
image: <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: go-go-fuzz-mayhemit-out-of-bounds
cmds:
  - cmd: /go/mayhemit.libfuzzer
    libfuzzer: true
1
2
3
4
5
6
7
image: $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds:latest
duration: 90
project: mayhem-examples
target: go-go-fuzz-mayhemit-out-of-bounds
cmds:
  - cmd: /go/mayhemit.libfuzzer
    libfuzzer: true

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

mayhemit-out-of-bounds-run

おめでとうございます! Mayhem がインデックス境界外の欠陥を発見しました。スクラッチから go-fuzz を使用した libFuzzer インストゥルメンテーション付き Go ターゲットをビルドし、Mayhem を使用してバグを検出できました。

✏️ まとめと振り返り

このレッスンでは、Mayhem で go-fuzz を使用した libFuzzer インストゥルメンテーション付き Go ターゲットをコンパイルし、テストする方法を学びました。


学習内容

1. Go-Fuzz を使用した LibFuzzer インストゥルメンテーション付き Go ターゲットをコンパイルおよびテストして到達可能なアサーションの欠陥を検出する。
  • ソース コードには次の欠陥が含まれているはずです:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    func mayhemit(bytes []byte) int {
        content := string(bytes)
        if len(content) >= 3 {
            if string(content[0]) == "b" {
                if string(content[1]) == "u" {
                    if string(content[2]) == "g" {
                        panic("found a bug!")
                    }
                }
            }
        }
        return 0
    }
    

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

    1
    2
    3
    4
    5
    6
    7
    8
    FROM fuzzers/go-fuzz:1.2.0
    COPY mayhemit.go .
    RUN go-fuzz-build -libfuzzer -o mayhemit.a && \
        clang -fsanitize=fuzzer mayhemit.a -o mayhemit.libfuzzer
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD ["/go/mayhemit.libfuzzer"]
    

    1
    2
    3
    4
    5
    image: <DOCKERHUB_USERNAME>/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    
    1
    2
    3
    4
    5
    image: $MAYHEM_DOCKER_REGISTRY/forallsecure/go-go-fuzz:latest
    project: mayhem-examples
    target: go-go-fuzz
    cmds:
      - cmd: /go/mayhemit.libfuzzer
    
2. Go-Fuzz を使用した LibFuzzer インストゥルメンテーション付き Go ターゲットをコンパイルおよびファジングしてインデック境界外の欠陥を検出する。
  • ソース コードには次の欠陥が含まれているはずです:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    func mayhemit(bytes []byte) int {
        content := string(bytes)
        if len(content) >= 3 && len(content) < 5 {
            if string(content[0]) == "b" {
                if string(content[1]) == "u" {
                    if string(content[2]) == "g" {
                        var x = content[10]
                        fmt.Println(x)
                    }
                }
            }
        }
        return 0
    }
    

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

    1
    2
    3
    4
    5
    6
    7
    8
    FROM fuzzers/go-fuzz:1.2.0
    COPY mayhemit.go .
    RUN go-fuzz-build -libfuzzer -o mayhemit.a && \
        clang -fsanitize=fuzzer mayhemit.a -o mayhemit.libfuzzer
    
    # Set to fuzz!
    ENTRYPOINT []
    CMD ["/go/mayhemit.libfuzzer"]
    

    1
    2
    3
    4
    5
    6
    7
    image: <DOCKERHUB_USERNAME>/go-go-fuzz-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: go-go-fuzz-mayhemit-out-of-bounds
    cmds:
      - cmd: /go/mayhemit.libfuzzer
        libfuzzer: true
    
    1
    2
    3
    4
    5
    6
    7
    image: $MAYHEM_DOCKER_REGISTRY/go-go-fuzz-mayhemit-out-of-bounds:latest
    duration: 90
    project: mayhem-examples
    target: go-go-fuzz-mayhemit-out-of-bounds
    cmds:
      - cmd: /go/mayhemit.libfuzzer
        libfuzzer: true