offensive-fuzzing について
セキュリティ研究者向けのファジング手法とツールのガイドです。ターゲット調査、テスト環境の構築、実行、クラッシュ分析を、手順やコマンド例とともに扱います。
主な機能
- 01ファジング方式とツールの比較
- 02ターゲット調査と作業手順
- 03ハーネスとシードコーパスの準備
- 04並列実行と停滞への対処
- 05クラッシュの分析と分類
- 06カーネル・言語別の参考情報
活用シーン
- 01ターゲットに合うファジング方式の選定
- 02C/C++ ソフトウェアのテスト構築
- 03検出クラッシュの分析と分類
- 04カーネルや言語別テストの計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ snailsploit/ claude-red --skill offensive-fuzzingインストール後、Claude Code で/offensive-fuzzingを入力して読み込まれたか確認します。
詳細
オフェンシブファジング
ファザーの種類
| 種類 | カバレッジ | 速度 | ツール |
|---|---|---|---|
| BlackBox | 低い | 高速 | Peach, Boofuzz |
| GreyBox | 良好 | 高速 | AFL++, Honggfuzz, libFuzzer, WinAFL |
| Snapshot | 良好 | 最速 | Nyx, wtf, Snapchange |
| WhiteBox | 最良 | 低速 | KLEE, QSYM, SymSan |
| Ensemble | 最良 | 高速 | AFL++ + Honggfuzz + libFuzzer |
GreyBox のサブタイプ: Directed(AFLGo, UAFuzz)、Grammar(AFLSmart, Tlspuffin)、Concolic(QSYM, Driller)、Kernel(syzkaller, kAFL, wtf)。
基本的なワークフロー
Research target → Choose analyses → Build harness → Seed corpus → Instrument → Fuzz → Triage crashes → Report
1. 対象を調査する
- すべての入力箇所(ファイル、ネットワーク、IPC、システムコール、IOCTL)を洗い出します
- 価値の高い領域を特定します。過去にパッチが適用されたコード、複雑なパーサー、新たに追加されたコード、入力の取り込み箇所などです
- カーネルモジュールの場合は、
copy_from_userだけでなく、DMA-BUF の操作、ページフォールトハンドラー、VM 操作構造体、割り当てコールバックも調べます
2. 計装してビルドする
# AFL++ (preferred for GreyBox)
CC=afl-clang-fast CXX=afl-clang-fast++ cmake -DCMAKE_BUILD_TYPE=Release .. && make -j
# libFuzzer + ASan/UBSan (C/C++)
cmake -DCMAKE_CXX_FLAGS="-fsanitize=fuzzer,address,undefined -O1 -g" ..
# CmpLog build for hard compares
AFL_LLVM_CMPLOG=1 CC=afl-clang-fast CXX=afl-clang-fast++ make clean all
Windows(MSVC): Project Properties → C/C++ → Address Sanitizer: Yes (/fsanitize=address)
3. ハーネスを作成する
libFuzzer(C++):
#include <cstdint>
#include <cstddef>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
parse_or_process(data, size);
return 0;
}
Honggfuzz HF_ITER(永続モード — 大規模な対象に推奨):
#include "honggfuzz.h"
int main(int argc, char** argv) {
initialize_target(); // runs once
for (;;) {
size_t len; uint8_t *buf;
HF_ITER(&buf, &len);
FILE* s = fmemopen(buf, len, "r");
target_function(s);
fclose(s);
reset_target_state();
}
}
AFL++ 永続モード(__AFL_LOOP):
while (__AFL_LOOP(10000)) {
// re-read input and process
}
macOS IPC(Mach メッセージファジング):
void *lib_handle = dlopen("libexample.dylib", RTLD_LAZY);
pFunction = dlsym(lib_handle, "DesiredFunction");
4. シードコーパスを作成する
- 対象のテストスイート、バグ報告、実際のサンプルから収集します
- ファイル形式については Web をクロール(Common Crawl)し、MIME タイプでフィルタリングします
- 最小化します:
afl-cmin -i raw_corpus -o seeds -- ./target @@ - 入力をトリミングします:
afl-tmin -i crash -o crash.min -- ./target @@
5. ファジングを開始する
AFL++ 並列実行(プライマリ + cmplog を使用するセカンダリ):
afl-fuzz -M f1 -i seeds -o findings -x dict.txt -- ./target @@
afl-fuzz -S s1 -i seeds -o findings -c 0 -- ./target @@
libFuzzer:
./target_libfuzzer corpus/ -max_total_time=3600 -workers=4
バイナリのみ(QEMU):
afl-fuzz -Q -i seeds -o findings -- target.exe @@
スナップショット(AFL++ Nyx):
NYX_MODE=1 AFL_MAP_SIZE=1048576 afl-fuzz -i seeds -o findings -- ./target_nyx @@
Ensemble(AFL++ + Honggfuzz でコーパスを共有):
# Terminal 1
afl-fuzz -M fuzzer1 -i seeds -o sync_dir -- ./target @@
# Terminal 2
../honggfuzz/honggfuzz -i sync_dir/fuzzer1/queue -W sync_dir/hfuzz \
--linux_perf_ipt_block -t 10 -- ./target ___FILE___
6. 進捗を監視し、停滞を解消する
進捗が停滞した場合:
- AFL++ のセカンダリで CmpLog を有効にします:
-c 0 - 辞書を追加します:
-x dict.txtまたはAFL_TOKEN_FILE - 特定の BB/関数を対象に、Directed ファジング(AFLGo)へ切り替えます
- 難しい分岐では、コンコリック実行による支援(QSYM, Driller)を使用します
- 対象をスナップショット化して exec/s を増やします
- MOpt スケジューラーでは
AFL_MAP_SIZE=1048576、-L 0を指定します
7. クラッシュをトリアージする
# 1. Minimize
afl-tmin -i crash -o crash.min -- ./target @@
# 2. Symbolize
ASAN_OPTIONS=abort_on_error=1:symbolize=1 ./target crash.min 2>asan.log
# 3. Hash + bucket
./cov-tool --bbids ./target crash.min > cov.hash
./bucket.py --key "$(cat cov.hash)" --log asan.log --out triage/
サニタイザー環境変数のクイックリファレンス:
ASAN_OPTIONS=abort_on_error=1:symbolize=1:detect_stack_use_after_return=1
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1
TSAN_OPTIONS=halt_on_error=1:history_size=7
MSAN_OPTIONS=poison_in_dtor=1:track_origins=2
オラクルの選択
| バグの種類 | オラクル |
|---|---|
| メモリー安全性 | ASan, HWASan(AArch64、オーバーヘッドが低い) |
| 初期化されていない読み取り | MSan |
| 並行処理 | TSan |
| 未定義動作 | UBSan |
| 型安全性 | TypeSan |
| ヒープの強化 | Scudo Hardened Allocator |
| ロジックバグ | 差分/冪等性オラクル |
| カーネルメモリー | KASAN, KMSAN, KCSAN |
| カーネルの未定義動作 | KUBSan(CONFIG_UBSAN_TRAP=y) |
| CFI | KCFI(-fsanitize=kcfi、Clang 18) |
| バイナリのみ | QASAN(QEMU+ASan)、DynamoRIO |
プロパティオラクルのパターン:
- 冪等性:
f(x) == f(f(x)) - 差分:2 つの実装を比較し、出力の不一致で分類します
- 不変条件:単調な長さ、チェックサムの一致、解析後のスキーマ検証
特殊な対象
カーネル(Linux)— syzkaller
{
"target": "linux/arm64",
"http": ":56700",
"workdir": "/path/to/workdir",
"kernel_obj": "/path/to/kernel",
"image": "/path/to/rootfs.ext3",
"sshkey": "/path/to/id_rsa",
"procs": 8,
"enable_syscalls": ["openat$module_name", "ioctl$IOCTL_CMD", "mmap"],
"type": "qemu",
"vm": { "count": 4, "cpu": 2, "mem": 2048 }
}
- 特定のサブシステムのカバレッジを深めるため、
enable_syscallsを制限します syz-extractを使用して、カスタムモジュール向けの定数を取得しますCONFIG_KASAN=y、CONFIG_KCFI=y、CONFIG_DEBUG_INFO_BTF=yを有効にしますkcovフィルターとsyz_cover_filterを使用してカバレッジを誘導します- ネットワークファジング:
TUN/TAPと疑似システムコール(syz_emit_ethernet)を介して注入します - クラッシュのデコード:
./scripts/decode_stacktrace.sh vmlinux ... < dmesg.log
syzkaller の再現ケース:
syz-execprog -repeat=0 -procs=1 -cover=0 -debug target.repro
EDR/Windows スキャンエンジン
WTF スナップショットハーネスのひな形(mpengine.dll / mini-filter):
g_Backend->SetBreakpoint("nt!KeBugCheck2", [](Backend_t *Backend) {
const uint64_t BCode = Backend->GetArg(0);
Backend->Stop(Crash_t(fmt::format("crash-{:#x}", BCode)));
});
FilterConnectionPort のファジング:
HANDLE hPort;
FilterConnectCommunicationPort(L"\\PortName", 0, NULL, 0, NULL, &hPort);
FilterSendMessage(hPort, fuzzData, sizeof(fuzzData), NULL, 0, &bytesReturned);
IOCTL ファジングのパターン:
HANDLE hDev = CreateFile(L"\\\\.\\DeviceName", GENERIC_READ|GENERIC_WRITE, ...);
DeviceIoControl(hDev, ioctlCode, inputBuf, inputLen, outBuf, outLen, &ret, NULL);
- 初期化後、解析/ディスパッチループの直前にスナップショットを取得します
- カバレッジを可視化するには IDA Lighthouse を使用します
DRIVER_VERIFIER_DETECTED_VIOLATION (0xc4)、IRQL_NOT_LESS_OR_EQUAL (0xa)を監視します- WinDbg:
.symfix; !analyze -v; k; !heap -p -a @rax
Linux 上のクロスプラットフォーム mpengine.dll(loadlibrary + HF_ITER + Intel PT):
// Bypass Lua VM to avoid stability issues
insert_function_redirect((void*)luaV_execute_address, my_lua_exec, HOOK_REPLACE_FUNCTION);
for (;;) {
HF_ITER(&buf, &len);
ScanDescriptor.UserPtr = fmemopen(buf, len, "r");
__rsignal(&KernelHandle, RSIG_SCAN_STREAMBUFFER, &ScanParams, sizeof ScanParams);
}
Rust
# Full Rust fuzzing pipeline
cargo test # 1. property tests
cargo +nightly miri test # 2. UB via interpreter
cargo +nightly careful test # 3. runtime bounds checks
cargo fuzz run fuzz_target_1 -- -max_total_time=3600 # 4. libFuzzer crashes
RUSTFLAGS="--cfg loom" cargo test --release # 5. concurrency (if needed)
cargo fuzz coverage fuzz_target_1 # 6. coverage report
次の項目に関する unsafe ブロックに重点を置きます:Vec::from_raw_parts、境界チェックなしのインデックスアクセス、transmute のサイズ不一致、ポインター演算、FFI の整数切り詰め。
組み込み/バイナリのみ
- LibAFL:モジュール式の Rust フレームワーク。Unicorn エンジン、スナップショットモジュール、LBRFeedback(Intel でインストルメンテーションなし)、SAND による分離型サニタイズに対応
- Retrowrite / QASAN:ソースなしでカバレッジと ASan を実現するバイナリ書き換え
- Nautilus:構造化フォーマット向けの文法ベースのファジング
言語エコシステム
- Go 1.18+:
go test -fuzz=Fuzz -run=^$ ./... - Python:Atheris(CPython ネイティブ拡張のファジング)
- Rust:
cargo-fuzzまたはhonggfuzz-rs - JS エンジン:拡張インストルメンテーションを備えた Fuzzilli(PC の追跡には
__builtin_return_address(0)を使用) - Wasm ランタイム:V8/Wasmer/Wasmtime 間の差分ファジングには
wasmtime-fuzz、wafl - スマートコントラクト:Echidna、Foundry-fuzz(Solidity);Move-Fuzz(Aptos/Sui)
CI/CD 統合
- name: Build with afl-clang-fast
run: CC=afl-clang-fast make -j
- name: Fuzz (smoke, 15 min)
run: timeout 15m afl-fuzz -i seeds -o findings -- ./target @@ || true
- name: Upload crashes
if: always()
uses: actions/upload-artifact@v4
with:
path: findings/**/crashes/*
永続的な継続的ファジングには ClusterFuzzLite を使用し、実行間でコーパスをキャッシュします。
クラッシュ分析のクイックリファレンス
Linux:
ulimit -c unlimited && sysctl -w kernel.core_pattern=core.%e.%p
gdb -q ./target core.* -ex 'bt' -ex 'info reg' -ex q
addr2line -e ./target 0xDEADBEEF
Windows:
# Enable local dumps
New-Item 'HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps' -Force
# PageHeap
gflags /p /enable target.exe /full
カーネル KASAN/KMSAN:
dmesg -T | egrep -i 'kasan|kmsan' -A 60
./scripts/decode_stacktrace.sh vmlinux /lib/modules/$(uname -r)/build < dmesg.log
再現性: CPU ガバナーを固定し、安全な場合に限り ASLR を無効にし、RNG シードを固定します。永続モードでは入力シーケンスを保存し、クラッシュごとにバイナリのハッシュとサニタイザーのオプションを記録します。
ツール一覧
| ツール | 用途 |
|---|---|
| AFL++ | 汎用 GreyBox、CmpLog、MOpt、Nyx |
| Honggfuzz | Intel PT、クラッシュ検出、HF_ITER |
| libFuzzer | プロセス内、ソースコードが利用可能な場合 |
| syzkaller | Linux/Windows カーネルのシステムコールファジング |
| wtf | スナップショットファジング、Windows ターゲット |
| Nyx | AFL++ のスナップショットモード(Intel PT) |
| Snapchange | AWS のスナップショットファジング |
| LibAFL | カスタム Rust ファジングフレームワーク |
| AFLGo | 対象の BB/関数に向けた指向性ファジング |
| kAFL | カーネルおよび OS のファジング |
| Jackalope | バイナリのカバレッジガイド型ファジング(Windows/macOS) |
| cargo-fuzz | Rust と libFuzzer の統合 |
| Atheris | Python のファジング |
| Nautilus | 文法ベースのファジング |
| AFLTriage | クラッシュの自動トリアージ |
| afl-cov | AFL++ のカバレッジ分析 |
| ClusterFuzz | 分散ファジングインフラストラクチャ |