gosentry は、Go コードベースに対する最先端のファジングキャンペーンのための多数の機能を統合した、Go ツールチェーンのセキュリティ重視のフォークです。以前 go test -fuzz を使用していたなら、gosentry をその代替として使用してください。
Go ツールチェーンにはネイティブに存在しないさまざまなファジング改善とバグ検出器が含まれています。以下の TLDR を参照してください。関連するブログ記事は こちら から読むこともできます。
TLDR(機能とオプション):
struct 入力を直接ファジングします(カスタムパーサーは不要)。f.Add(Input{N: 7, S: "hi"}) でシードを追加し、f.Fuzz(func(t *testing.T, in Input) { ... }) を実行します。X + Y - Z は X / U + Z - 14 になり得ますが、X + Yè - Z にはなりませんcd src && ./make.bash # Produces ../bin/go. See GOFLAGS below.
> [!TIP]
> コントリビューター向けドキュメント: コードマップ、推奨される開発ループ、CI エントリポイント、ベンチマークスクリプトについては、`docs/gosentry/index.md` をお読みください。
> このフォークは Pull GitHub App を使用して、`golang/go:master` から `master` への PR を開き自動マージすることで、Go ツールチェーンの最新アップデートから遅れないようにしています。
## 機能 1: 構造体を意識したファジング(構造体を入力としてファズ)
#### 概要
Go のネイティブなファジング (`go test -fuzz=...`) は、ファズパラメータとして少数のスカラー型 (`[]byte`, `string`, 数値, ...) のみをサポートしています。gosentry では、それらのスカラーから構築された**複合型**(構造体、配列、スライス、ポインタ)もファズできます。
これは、コードが自然に構造化入力を受け取り、コーパスをシードしてミューテーションするためだけに独自のエンコーダ/デコーダを構築したくない場合に便利です。
例については、`test/gosentry/examples/multiargs` と `test/gosentry/examples/composite` を参照してください。
#### 簡単な例```go
type Input struct {
Data []byte
S string
N int
OK bool
}
func FuzzStructInput(f *testing.F) {
// Seed the initial corpus with a Go struct (gosentry feature).
f.Add(Input{Data: []byte("A"), S: "B", N: 7, OK: true})
f.Fuzz(func(t *testing.T, in Input) {
if in.OK && in.N == 1337 && in.S == "BOOMMOOB" && bytes.Equal(in.Data, []byte("A")) {
t.Fatalf("boom")
}
})
}
f.Add)と構造体ファジングの仕組み(作られた接着層)Go標準のファザーはstruct値を直接ファズできません(スカラー型の小さなリストしか変異できないため)。gosentryは小さな接着層を追加します。ファズターゲットが(Inputのような)複合型を使う場合、gosentryは舞台裏で単一の[]byteをファズします。実行のたびに、そのバイト列をあなたの構造体へデコードし(フィールド単位で、スライス/配列/ポインタは再帰的に処理)、デコードされた値をあなたのf.Fuzzコールバックへ渡します。シードにも同じエンコーディングが使われるため、f.Add(Input{...})はエンコードされた[]byteコーパスエントリになり、ファザーは他のシードと同様に再利用・変異できます。
ファザー(LibAFLを含む)は生のバイト列を変異させるため、どんなバイト列でも「何らかの」構造体値に変換し、処理を続行できるデコーダが必要です。JSON/gobはほとんどのランダム入力を拒否するため(カバレッジに悪影響)、また未エクスポートフィールドを設定しないため、ファジングでは不変条件を破る方が有益なことがよくあります。このカスタム形式は、小さく、高速で、決定的で、不正なデータに対して寛容です。
内部では、gosentry独自の単純なバイナリ形式(gobでもJSONでもない)を使用しています。コードはsrc/testing/libafl.goにあります:
libaflMarshalInputs / libaflAppendValuelibaflUnmarshalArgs / libaflDecodeValueエンコード規則(概要):
bool:1バイト(0または1)int/uintは8バイト)float32 = 4バイト、float64 = 8バイト)string:uvarint(len)に続けて生の文字列バイト列[]byte:uvarint(len)に続けて生のバイト列uvarint(len)に続けて各要素をエンコード0 = nil、1 = 存在する)に続けてポインタ先の値をエンコードこの作業は、以前開発された go-panikint から着想を得ています。整数演算のオーバーフロー/アンダーフロー検出と、(オプションで)整数変換時の型切り詰め検出を追加します。オーバーフローまたは切り詰めが検出されると、詳細なエラーメッセージを含むパニックが発生し、関与する特定の演算種別と整数型が示されます。
算術演算:符号付きおよび符号なし整数型の加算 +、減算 -、乗算 *、除算 / を処理します。符号付き整数では int8、int16、int32 を対象とします。符号なし整数では uint8、uint16、uint32、uint64 を対象とします。除算のケースでは、符号付き整数の MIN_INT / -1 オーバーフロー条件を特に検出します。int64 と uintptr は算術演算のチェック対象外です。
型切り詰め検出:情報を失う可能性のある整数型変換を検出します。すべての整数型(int8、int16、int32、int64、uint8、uint16、uint32、uint64)を対象とします。uintptr はプラットフォーム依存の使用法のため除外されます。これはデフォルトでは無効です。
オーバーフロー検出はデフォルトで有効です。無効にするには、./make.bash の前に GOFLAGS='-gcflags=-overflowdetect=false' を追加してください。切り詰め問題チェッカーを有効にするには、-gcflags=-truncationdetect=true を使用できます。
この機能はコンパイラのSSA生成をパッチし、整数演算と整数変換に追加のランタイムチェックを挿入します。バグが検出されると、詳細なエラーメッセージ付きのパニックを発生させるランタイム関数を呼び出します。チェックはソースコード位置に基づくフィルタリングで適用されるため、ユーザーコードだけが計測され、標準ライブラリファイルや依存関係(モジュールキャッシュおよびvendor/)はスキップされます。
関連するブログ記事は こちら で読めます。
該当する操作と同じ行、またはその直上の行にマーカーを追加すると、特定のレポートを抑制できます:
overflow_false_positivetruncation_false_positive例:```go // overflow_false_positive intentionalOverflow := a + b // truncation_false_positive x := uint8(big) sum2 := a + b // overflow_false_positive x2 := uint8(big) // truncation_false_positive
これが機能しない場合がありますが、それはGoが関数をインライン化しているためです。`// overflow_false_positive` で不十分な場合は、関数シグネチャの前に `//go:noinline` を追加してください。
## Feature 3: Panic on selected functions
ファジングの対象を調査する際、特定の関数が呼び出されたときにパニックを発生させたい場合があります。例えば、一部のソフトウェアはパニックを起こさずに `log.error` メッセージを出力する場合がありますが、そのような状況は、セキュリティ研究者がファジング中に検出したい状態を示していることがよくあります。
しかし、これらのエラーは通常、内部的に処理されるため(再試行や一時停止のメカニズム、またはログへのメッセージ出力など)、ファザーからはほぼ見えません。この機能の目的は、この問題に対処することです。
#### 使い方
gosentry をコンパイルし、`--panic-on` フラグを使用します。```bash
./bin/go test -fuzz=FuzzHarness --use-libafl --focus-on-new-code=false --catch-races=false --catch-leaks=false --panic-on="test_go_panicon.(*Logger).Warning,test_go_panicon.(*Logger).Error"
上記の例では、(*Logger).Warning または (*Logger).Error のいずれかが呼び出されるとパニックが発生します(カンマ区切りのリスト)。