关于 implementing-fuzz-testing-in-cicd-with-aflplusplus
介绍如何将 AFL++ 覆盖引导模糊测试集成到编译应用的 CI/CD 中。内容包括测试 harness、插桩、语料准备、并行测试和崩溃分析。
主要功能
- 01覆盖引导的输入变异测试
- 02源码和二进制插桩模式
- 03用于加速模糊测试的持久模式
- 04种子语料最小化
- 05CI/CD 与夜间测试配置
- 06崩溃复现与分类分析
适用场景
- 01测试处理不可信输入的解析器
- 02在 CI/CD 中定期运行模糊测试
- 03对无源码二进制进行模糊测试
- 04复现并整理发现的崩溃
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill implementing-fuzz-testing-in-cicd-with-aflplusplus安装后在 Claude Code 中输入/implementing-fuzz-testing-in-cicd-with-aflplusplus确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEassets/template.mdreferences/api-reference.mdstandards.mdworkflows.mdscripts/agent.pyprocess.py详细介绍
在 CI/CD 中使用 AFL++ 实施模糊测试
概述
AFL++(American Fuzzy Lop Plus Plus)是 AFL 的社区维护分支,提供先进的覆盖率引导模糊测试,用于发现编译型应用程序中的漏洞。AFL++ 使用遗传算法变异输入,跟踪代码覆盖率以发现触发崩溃、挂起和未定义行为的新执行路径。在 CI/CD 环境中,可以集成 AFL++ 来持续测试解析器、协议处理器、文件格式处理器以及任何处理不受信任输入的代码。AFL++ 支持持久模式以实现高速模糊测试(每秒高达 100,000+ 次执行)、自定义变异器、用于仅二进制模糊测试的 QEMU 模式,以及用于自动字典提取的 CmpLog/RedQueen。
何时使用
- 在你的环境中部署或配置使用 aflplusplus 在 CI/CD 中实施模糊测试的能力时
- 建立符合合规要求的安全控制时
- 为该领域构建或改进安全架构时
- 执行需要此实施的安全评估时
前提条件
- 基于 Linux 的 CI 运行器(AFL++ 不原生支持 Windows)
- GCC 或 Clang 编译器工具链
- 已安装 AFL++(
apt install aflplusplus或从源码构建) - 目标应用程序具有隔离输入处理的 harness 函数
- 有效输入样本的种子语料库
核心概念
覆盖率引导模糊测试
AFL++ 在编译时(或对于仅二进制目标通过 QEMU/Frida)对目标二进制文件进行插桩,以跟踪每个输入所执行的代码路径。当变异输入触发新代码路径时,它会被保存到语料库中以供进一步变异。这种反馈循环使 AFL++ 能够系统地探索程序状态空间。
插桩模式
| 模式 | 使用场景 | 性能 |
|---|---|---|
afl-clang-fast (LTO) | 有源码,最佳性能 | 最高 |
afl-clang-fast | 有源码,标准 | 高 |
afl-gcc-fast | 基于 GCC 的项目 | 高 |
QEMU mode | 仅二进制,无源码 | 中 |
Frida mode | 仅二进制,跨平台 | 中 |
Unicorn mode | 固件,嵌入式 | 低 |
持久模式
持久模式通过在循环内进行模糊测试来避免 fork 开销:
#include <unistd.h>
__AFL_FUZZ_INIT();
int main() {
__AFL_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
// Process buf[0..len-1]
parse_input(buf, len);
}
return 0;
}
工作流
步骤 1 --- 构建模糊测试 Harness
创建一个将 AFL++ 输入提供给目标函数的 harness:
// fuzz_harness.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "target_parser.h"
__AFL_FUZZ_INIT();
int main() {
__AFL_INIT();
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
int len = __AFL_FUZZ_TESTCASE_LEN;
if (len < 4) continue;
// Reset state between iterations
parser_context_t ctx;
parser_init(&ctx);
parser_process(&ctx, buf, len);
parser_cleanup(&ctx);
}
return 0;
}
步骤 2 --- 使用 AFL++ 插桩进行编译
# Standard instrumentation
export CC=afl-clang-fast
export CXX=afl-clang-fast++
# Enable AddressSanitizer for better crash detection
export AFL_USE_ASAN=1
# Build the target with instrumentation
$CC -o fuzz_harness fuzz_harness.c -ltarget_parser -fsanitize=address
# Build a CmpLog binary for better coverage
$CC -o fuzz_harness_cmplog fuzz_harness.c -ltarget_parser \
-fsanitize=address -DCMPLOG
步骤 3 --- 准备种子语料库
mkdir -p corpus/
# Add valid input samples
cp test_inputs/* corpus/
# Minimize the corpus
afl-cmin -i corpus/ -o corpus_min/ -- ./fuzz_harness @@
# Further minimize individual inputs
mkdir -p corpus_tmin/
for f in corpus_min/*; do
afl-tmin -i "$f" -o "corpus_tmin/$(basename $f)" -- ./fuzz_harness @@
done
步骤 4 --- 配置 CI/CD 集成
GitHub Actions:
name: Fuzz Testing
on:
push:
branches: [main]
schedule:
- cron: '0 2 * * *' # Nightly fuzzing
jobs:
fuzz:
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: Install AFL++
run: |
sudo apt-get update
sudo apt-get install -y aflplusplus
- name: Restore corpus cache
uses: actions/cache@v4
with:
path: corpus/
key: fuzz-corpus-${{ github.sha }}
restore-keys: fuzz-corpus-
- name: Build fuzzing harness
run: |
export CC=afl-clang-fast
export AFL_USE_ASAN=1
make fuzz_harness
- name: Run AFL++ fuzzing (CI mode)
env:
AFL_CMPLOG_ONLY_NEW: 1
AFL_FAST_CAL: 1
AFL_NO_STARTUP_CALIBRATION: 1
run: |
mkdir -p findings/
timeout 7200 afl-fuzz \
-S ci_fuzzer \
-i corpus/ \
-o findings/ \
-t 5000 \
-- ./fuzz_harness @@ || true
- name: Check for crashes
run: |
CRASHES=$(find findings/ -path "*/crashes/*" -not -name "README.txt" | wc -l)
echo "Found $CRASHES unique crashes"
if [ "$CRASHES" -gt 0 ]; then
echo "::error::AFL++ found $CRASHES crashes"
for crash in findings/*/crashes/*; do
[ -f "$crash" ] && echo "Crash: $crash ($(wc -c < $crash) bytes)"
done
exit 1
fi
- name: Update corpus cache
if: always()
run: |
afl-cmin -i findings/ci_fuzzer/queue/ -o corpus/ -- ./fuzz_harness @@
步骤 5 --- 用于夜间运行的并行模糊测试
# Launch multiple secondary instances for better coverage
for i in $(seq 1 $(nproc)); do
afl-fuzz -S fuzzer_$i \
-i corpus/ \
-o findings/ \
-- ./fuzz_harness @@ &
done
# Wait for all fuzzers
wait
# Merge and minimize corpus
afl-cmin -i findings/*/queue/ -o corpus_merged/ -- ./fuzz_harness @@
步骤 6 --- 崩溃分类
# Reproduce and categorize crashes
for crash in findings/*/crashes/*; do
echo "=== Testing: $crash ==="
timeout 5 ./fuzz_harness_asan "$crash" 2>&1 | head -20
echo "---"
done
# Deduplicate crashes by stack trace
afl-collect findings/ crashes_deduped/ -- ./fuzz_harness @@
AFL++ 的 CI/CD 最佳实践
| 设置 | CI 短时运行 | 夜间长时运行 |
|---|---|---|
| 持续时间 | 30-60 分钟 | 4-24 小时 |
| 模式 | -S(仅 secondary) | -S(CI 不使用 -M) |
AFL_CMPLOG_ONLY_NEW | 1 | 1 |
AFL_FAST_CAL | 1 | 0 |
AFL_NO_STARTUP_CALIBRATION | 1 | 0 |
| 语料库缓存 | 必需 | 必需 |
| 并行实例 | 1-2 | nproc |
监控模糊测试活动
# View fuzzing statistics
afl-whatsup findings/
# Key metrics to track:
# - Total paths found (code coverage indicator)
# - Unique crashes / unique hangs
# - Stability percentage (should be >90%)
# - Exec speed (execs/sec)
# - Cycles done (full corpus cycles completed)
参考资料
评论
支持 Markdown