CtrlK
BlogDocsLog inGet started
Tessl Logo

fuzzing

Coverage-guided fuzzing methodology for compiled binaries and libraries: target scoping, fuzzer selection (AFL++/libFuzzer/Honggfuzz), harness skeleton, ASan+UBSan flags, corpus curation, crash triage and minimization (afl-tmin/minimize_corpus), exploitability rubric. Outputs corpora and crashes to /workspace/. Triggers on: 'fuzz', 'fuzzing', 'AFL', 'libFuzzer', 'harness', 'crash triage', 'coverage-guided', 'afl-tmin', 'sanitizer', 'corpus'.

74

Quality

92%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Coverage-Guided Fuzzing Playbook

Find memory-corruption and logic bugs by systematically mutating inputs while tracking code coverage. Works on parsers, network protocol handlers, file format readers, crypto implementations.

1. Target Scoping

Before writing a harness, answer:

  • What is the attack surface? (file parser / network socket / library API)
  • What input format does the target consume? (binary struct / text / TLV)
  • Is source available? (instrumented build) or binary-only? (QEMU/Frida mode)
# Quick API surface for a library
nm -D /workspace/target.so | grep ' T ' | awk '{print $3}' | sort > /workspace/exports.txt
# or via radare2
r2 -q -e bin.demangle=true -c "aaa; afl~pub" /workspace/target.so 2>/dev/null | head -40

2. Fuzzer Selection

ScenarioFuzzerRationale
Source available, C/C++AFL++Best coverage feedback, mutator ecosystem
Source available, any langlibFuzzerIn-process, lower overhead, LLVM native
Binary-onlyAFL++ QEMU modeAFL_USE_QEMU=1
Binary-only (faster)AFL++ Frida modeLighter than QEMU on some targets
Multi-CPU farmAFL++ + honggfuzzhonggfuzz for socket fuzzing
Go / Rustnative go-fuzz / cargo fuzzLanguage-native instrumentation
Network protocol daemonAFL++ persistent + preenyDesock the accept() loop

3. Instrumented Build (source-available)

# AFL++
CC=afl-clang-fast CXX=afl-clang-fast++ \
  cmake -DCMAKE_BUILD_TYPE=Debug \
        -DCMAKE_C_FLAGS="-fsanitize=address,undefined -g -O1" \
        -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -g -O1" \
        -S /workspace/src -B /workspace/build-afl
cmake --build /workspace/build-afl -j$(nproc)

# libFuzzer (alternative)
CC=clang CXX=clang++ \
  cmake -DCMAKE_BUILD_TYPE=Debug \
        -DCMAKE_C_FLAGS="-fsanitize=fuzzer,address,undefined -g" \
        -DCMAKE_CXX_FLAGS="-fsanitize=fuzzer,address,undefined -g" \
        -S /workspace/src -B /workspace/build-lf
cmake --build /workspace/build-lf -j$(nproc)

Minimum sanitizer flags: -fsanitize=address,undefined. Add -fsanitize=memory (MSan) on a separate build for uninit reads. Do NOT combine MSan with ASan — they conflict.

4. Harness Skeleton

AFL++ persistent-mode harness (C)

/* harness.c — compile with afl-clang-fast */
#include <stdint.h>
#include <string.h>

/* Include target headers */
#include "target_api.h"

/* AFL++ persistent mode — loops inside the same process (~20x faster) */
__AFL_FUZZ_INIT();

int main(void) {
    __AFL_INIT();
    const uint8_t *buf = __AFL_FUZZ_TESTCASE_BUF;
    while (__AFL_LOOP(10000)) {
        size_t len = __AFL_FUZZ_TESTCASE_LEN;
        /* Call the target function directly */
        target_parse(buf, len);   /* replace with actual API */
    }
    return 0;
}

libFuzzer target (C)

/* fuzz_target.cc */
#include <stdint.h>
#include <stddef.h>
#include "target_api.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    /* Wrap any setup in thread_local or static init if needed */
    target_parse(data, size);
    return 0;  /* non-zero aborts fuzzer */
}

Key harness rules:

  • Allocate nothing that leaks between iterations (use arena or reset)
  • Do not call exit() or abort() inside the harness — let ASan terminate
  • Minimize initialization overhead outside the fuzz loop
  • If target needs a null-terminated string: strndup((char*)data, size)

5. Seed Corpus Curation

# Create corpus dir
mkdir -p /workspace/corpus/initial

# If target is a file parser, seed with real samples
find /usr/share -name "*.png" 2>/dev/null | head -10 | \
  xargs -I{} cp {} /workspace/corpus/initial/

# For binary protocols, create minimal valid inputs by hand
# For text protocols, minimal valid requests:
printf 'GET / HTTP/1.0\r\n\r\n' > /workspace/corpus/initial/http_get.bin

# AFL++ minimize corpus to remove redundant seeds
afl-cmin -i /workspace/corpus/initial \
         -o /workspace/corpus/minimized \
         -- /workspace/build-afl/harness @@

Good seed = valid, minimal, diverse. 5–50 seeds beats 5000 random bytes.

6. Run AFL++

mkdir -p /workspace/findings/afl

# Single-core
AFL_SKIP_CPUFREQ=1 \
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 \
afl-fuzz \
  -i /workspace/corpus/minimized \
  -o /workspace/findings/afl \
  -t 1000 \           # timeout per testcase (ms)
  -- /workspace/build-afl/harness @@

# Multi-core (1 main + N-1 secondary)
# Main:
afl-fuzz -M main-fuzzer -i /workspace/corpus/minimized \
  -o /workspace/findings/afl -- /workspace/build-afl/harness @@
# Secondary (run in parallel):
afl-fuzz -S sec-01 -i /workspace/corpus/minimized \
  -o /workspace/findings/afl -- /workspace/build-afl/harness @@

Key AFL++ environment flags

FlagEffect
AFL_USE_ASAN=1Enable ASan in afl-fuzz without recompile
AFL_DISABLE_TRIM=1Skip trim step (faster iteration)
AFL_FAST_CAL=1Reduce calibration cycles
AFL_CUSTOM_MUTATOR_LIBRARYLoad grammar/structure-aware mutator
AFL_LLVM_CMPLOG=1 + -c cmplog_binaryToken extraction from cmp instructions

7. Run libFuzzer

mkdir -p /workspace/findings/lf /workspace/corpus/lf-work

# Merge initial corpus into working corpus first
/workspace/build-lf/fuzz_target \
  -merge=1 \
  /workspace/corpus/lf-work \
  /workspace/corpus/minimized

# Fuzz with ASAN_OPTIONS for cleaner reports
ASAN_OPTIONS=halt_on_error=1:print_stats=1 \
/workspace/build-lf/fuzz_target \
  -max_total_time=3600 \
  -artifact_prefix=/workspace/findings/lf/ \
  -jobs=$(nproc) \
  -workers=$(nproc) \
  /workspace/corpus/lf-work

8. Crash Triage

Reproduce a crash

# AFL++ crash
ASAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
/workspace/build-afl/harness /workspace/findings/afl/default/crashes/id:000000*

# libFuzzer crash
ASAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
/workspace/build-lf/fuzz_target /workspace/findings/lf/crash-*

Minimize crash (AFL++)

afl-tmin \
  -i /workspace/findings/afl/default/crashes/id:000000 \
  -o /workspace/findings/crash-min.bin \
  -- /workspace/build-afl/harness @@

Parse crash output

ASan report prefixBug classExploitability
heap-buffer-overflowOOB read/write on heapWrite → HIGH, Read → MEDIUM
stack-buffer-overflowOOB on stackOften HIGH (canary missing → RCE)
heap-use-after-freeUAFHIGH — check if write capability
global-buffer-overflowOOB on globalContext-dependent
null-dereferenceNULL derefLOW-MEDIUM (usually DoS)
double-freeDouble freeHIGH in glibc < 2.35
UBSan signed-integer-overflowInteger overflowContext-dependent
UBSan shift-exponentUndefined shiftUsually LOW

9. Exploitability Rubric

Assess each unique crash:

  1. Is it write or read? Write-anywhere = HIGH. Read = may still be HIGH (information leak) or MEDIUM (crash only).
  2. Is address attacker-controlled? Check if addr in the ASan report correlates with bytes from the fuzz input.
  3. Is there a canary? checksec --file=/workspace/build-afl/harness. No canary on a stack overflow = trivially exploitable.
  4. Unique crash count: Run afl-whatsup -s /workspace/findings/afl/. Deduplicate by first 3 frames of the stack trace.
# Deduplicate crashes by backtrace hash (quick)
for f in /workspace/findings/afl/default/crashes/id:*; do
  ASAN_OPTIONS=halt_on_error=1 \
  /workspace/build-afl/harness "$f" 2>&1 | \
  grep "^    #[0-2] " | md5sum | cut -d' ' -f1
done | sort | uniq -c | sort -rn | head -20

10. Persist and validate crash evidence

For each unique crash, preserve:

  • the minimized input and its SHA-256;
  • complete sanitizer output and the first stable stack frames;
  • pinned compiler, sanitizer, and target-binary build identity;
  • the exact reproduction command;
  • a benign corpus input run through the same harness.

Use validate_workspace_finding with the crash signature as the positive signal and the benign execution as the negative control. Save its JSON output under findings/evidence/ before creating a finding. A sanitizer line alone is a triage lead; a reportable issue needs a reproducible minimized input and a baseline that does not emit the same signature.

Tools Quick Reference

ToolInstallUse
afl-fuzzapt install afl++Coverage-guided mutation fuzzer
afl-clang-fastbundled with afl++Instrumented compilation
afl-tminbundledTest case minimization
afl-cminbundledCorpus minimization
afl-whatsupbundledMulti-instance status summary
libFuzzerclang built-inIn-process fuzzer (LLVM)
cargo fuzzcargo install cargo-fuzzRust fuzzing (libFuzzer backend)
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.