L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Développement · 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

Documentation de la Skill

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%)
# - 执行速度(次/秒)
# - 完成的循环数(完整语料库循环次数)

参考资料

Skills enregistrées