UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
開発・技術 · SKILL.md

implementing-fuzz-testing-in-cicd-with-aflplusplus

implementing-fuzz-testing-in-cicd-with-aflplusplus

将 AFL++ 覆盖率引导的模糊测试集成到 CI/CD 管道中,以发现 C/C++ 和编译型应用中的内存损坏、输入处理和逻辑漏洞。

Independent GitHub snapshot · Automated classification · Original documentation governs

スキルの説明

GitHub

name: implementing-fuzz-testing-in-cicd-with-aflplusplus description: 将 AFL++ 覆盖率引导的模糊测试集成到 CI/CD 管道中,以发现 C/C++ 和编译型应用中的内存损坏、输入处理和逻辑漏洞。 domain: cybersecurity subdomain: devsecops tags: [aflplusplus, fuzz-testing, cicd, coverage-guided-fuzzing, security-testing, vulnerability-discovery, afl] version: "1.0" author: mahipal license: Apache-2.0

在 CI/CD 中使用 AFL++ 实施模糊测试

概述

AFL++(American Fuzzy Lop Plus Plus)是 AFL 的社区维护分支,为发现编译型应用中的漏洞提供最先进的覆盖率引导模糊测试。AFL++ 使用遗传算法变异输入,跟踪代码覆盖率以寻找触发崩溃、挂起和未定义行为的新执行路径。在 CI/CD 环境中,AFL++ 可以集成到持续测试解析器、协议处理程序、文件格式处理器以及任何处理不受信任输入的代码中。AFL++ 支持持久模式以实现高速模糊测试(每秒超过 100,000 次执行)、自定义变异器、用于纯二进制模糊测试的 QEMU 模式以及用于自动字典提取的 CmpLog/RedQueen。

前置条件

  • 基于 Linux 的 CI Runner(AFL++ 原生不支持 Windows)
  • GCC 或 Clang 编译器工具链
  • 已安装 AFL++(apt install aflplusplus 或从源码构建)
  • 带有隔离输入处理的测试线束函数的目标应用
  • 有效输入样本的种子语料库

核心概念

覆盖率引导模糊测试

AFL++ 在编译时(或通过 QEMU/Frida 对纯二进制目标)对目标二进制文件进行插桩,以跟踪每个输入执行了哪些代码路径。当变异输入触发新的代码路径时,它会被保存到语料库中以供进一步变异。这种反馈循环使 AFL++ 能够系统地探索程序状态空间。

插桩模式

模式使用场景性能
afl-clang-fast(LTO)有源码,最佳性能最高
afl-clang-fast有源码,标准高
afl-gcc-fastGCC 项目高
QEMU 模式纯二进制,无源码中
Frida 模式纯二进制,跨平台中
Unicorn 模式固件,嵌入式低

持久模式

持久模式通过在循环中进行模糊测试来避免 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 — 构建模糊测试线束

创建一个将 AFL++ 输入传递给目标函数的测试线束:

// 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++ 插桩编译

# 标准插桩
export CC=afl-clang-fast
export CXX=afl-clang-fast++

# 启用 AddressSanitizer 以更好地检测崩溃
export AFL_USE_ASAN=1

# 使用插桩构建目标
$CC -o fuzz_harness fuzz_harness.c -ltarget_parser -fsanitize=address

# 构建 CmpLog 二进制文件以获得更好的覆盖率
$CC -o fuzz_harness_cmplog fuzz_harness.c -ltarget_parser \
  -fsanitize=address -DCMPLOG

步骤 3 — 准备种子语料库

mkdir -p corpus/
# 添加有效输入样本
cp test_inputs/* corpus/
# 最小化语料库
afl-cmin -i corpus/ -o corpus_min/ -- ./fuzz_harness @@
# 进一步最小化单个输入
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 * * *'  # 每夜模糊测试

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 — 夜间运行的并行模糊测试

# 启动多个次级实例以获得更好的覆盖率
for i in $(seq 1 $(nproc)); do
    afl-fuzz -S fuzzer_$i \
      -i corpus/ \
      -o findings/ \
      -- ./fuzz_harness @@ &
done

# 等待所有模糊测试器
wait

# 合并和最小化语料库
afl-cmin -i findings/*/queue/ -o corpus_merged/ -- ./fuzz_harness @@

步骤 6 — 崩溃分析

# 重现和分类崩溃
for crash in findings/*/crashes/*; do
    echo "=== Testing: $crash ==="
    timeout 5 ./fuzz_harness_asan "$crash" 2>&1 | head -20
    echo "---"
done

# 按堆栈跟踪去重崩溃
afl-collect findings/ crashes_deduped/ -- ./fuzz_harness @@

AFL++ CI/CD 最佳实践

设置CI 短时运行夜间长时运行
时长30-60 分钟4-24 小时
模式-S(仅次级)-S(CI 不用 -M)
AFL_CMPLOG_ONLY_NEW11
AFL_FAST_CAL10
AFL_NO_STARTUP_CALIBRATION10
语料库缓存必需必需
并行实例1-2nproc

监控模糊测试活动

# 查看模糊测试统计
afl-whatsup findings/

# 需要跟踪的关键指标:
# - 找到的路径总数(代码覆盖率指标)
# - 唯一崩溃数 / 唯一挂起数
# - 稳定性百分比(应 >90%)
# - 执行速度(次/秒)
# - 完成的循环数(完整语料库循环次数)

参考资料

保存したスキル