Anthropic-Cybersecurity-Skills implementing-fuzz-testing-in-cicd-with-aflplusplus
Integrate AFL++ coverage-guided fuzz testing into CI/CD pipelines to discover memory corruption, input handling,
git clone https://github.com/mukul975/Anthropic-Cybersecurity-Skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/mukul975/Anthropic-Cybersecurity-Skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/implementing-fuzz-testing-in-cicd-with-aflplusplus" ~/.claude/skills/mukul975-anthropic-cybersecurity-skills-implementing-fuzz-testing-in-cicd-with-a && rm -rf "$T"
skills/implementing-fuzz-testing-in-cicd-with-aflplusplus/SKILL.mdImplementing Fuzz Testing in CI/CD with AFL++
Overview
AFL++ (American Fuzzy Lop Plus Plus) is a community-maintained fork of AFL that provides state-of-the-art coverage-guided fuzz testing for discovering vulnerabilities in compiled applications. AFL++ uses genetic algorithms to mutate inputs, tracking code coverage to find new execution paths that trigger crashes, hangs, and undefined behavior. In CI/CD environments, AFL++ can be integrated to continuously test parsers, protocol handlers, file format processors, and any code that handles untrusted input. AFL++ supports persistent mode for high-speed fuzzing (up to 100,000+ executions per second), custom mutators, QEMU mode for binary-only fuzzing, and CmpLog/RedQueen for automatic dictionary extraction.
When to Use
- When deploying or configuring implementing fuzz testing in cicd with aflplusplus capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Linux-based CI runners (AFL++ does not support Windows natively)
- GCC or Clang compiler toolchain
- AFL++ installed (
or built from source)apt install aflplusplus - Target application with harness functions isolating input processing
- Seed corpus of valid input samples
Core Concepts
Coverage-Guided Fuzzing
AFL++ instruments the target binary at compile time (or via QEMU/Frida for binary-only targets) to track which code paths each input exercises. When a mutated input triggers a new code path, it is saved to the corpus for further mutation. This feedback loop enables AFL++ to systematically explore program state space.
Instrumentation Modes
| Mode | Use Case | Performance |
|---|---|---|
(LTO) | Source available, best performance | Highest |
| Source available, standard | High |
| GCC-based projects | High |
| Binary-only, no source | Medium |
| Binary-only, cross-platform | Medium |
| Firmware, embedded | Low |
Persistent Mode
Persistent mode avoids fork overhead by fuzzing within a loop:
#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; }
Workflow
Step 1 --- Build the Fuzzing Harness
Create a harness that feeds AFL++ input to the target function:
// 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; }
Step 2 --- Compile with AFL++ Instrumentation
# 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
Step 3 --- Prepare Seed Corpus
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
Step 4 --- Configure CI/CD Integration
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 @@
Step 5 --- Parallel Fuzzing for Nightly Runs
# 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 @@
Step 6 --- Crash Triage
# 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 @@
CI/CD Best Practices for AFL++
| Setting | CI Short Run | Nightly Long Run |
|---|---|---|
| Duration | 30-60 min | 4-24 hours |
| Mode | (secondary only) | (no for CI) |
| 1 | 1 |
| 1 | 0 |
| 1 | 0 |
| Corpus caching | Required | Required |
| Parallel instances | 1-2 | nproc |
Monitoring Fuzzing Campaigns
# 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)