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'.
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
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.
Before writing a harness, answer:
# 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| Scenario | Fuzzer | Rationale |
|---|---|---|
| Source available, C/C++ | AFL++ | Best coverage feedback, mutator ecosystem |
| Source available, any lang | libFuzzer | In-process, lower overhead, LLVM native |
| Binary-only | AFL++ QEMU mode | AFL_USE_QEMU=1 |
| Binary-only (faster) | AFL++ Frida mode | Lighter than QEMU on some targets |
| Multi-CPU farm | AFL++ + honggfuzz | honggfuzz for socket fuzzing |
| Go / Rust | native go-fuzz / cargo fuzz | Language-native instrumentation |
| Network protocol daemon | AFL++ persistent + preeny | Desock the accept() loop |
# 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.
/* 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;
}/* 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:
exit() or abort() inside the harness — let ASan terminatestrndup((char*)data, size)# 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.
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 @@| Flag | Effect |
|---|---|
AFL_USE_ASAN=1 | Enable ASan in afl-fuzz without recompile |
AFL_DISABLE_TRIM=1 | Skip trim step (faster iteration) |
AFL_FAST_CAL=1 | Reduce calibration cycles |
AFL_CUSTOM_MUTATOR_LIBRARY | Load grammar/structure-aware mutator |
AFL_LLVM_CMPLOG=1 + -c cmplog_binary | Token extraction from cmp instructions |
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# 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-*afl-tmin \
-i /workspace/findings/afl/default/crashes/id:000000 \
-o /workspace/findings/crash-min.bin \
-- /workspace/build-afl/harness @@| ASan report prefix | Bug class | Exploitability |
|---|---|---|
heap-buffer-overflow | OOB read/write on heap | Write → HIGH, Read → MEDIUM |
stack-buffer-overflow | OOB on stack | Often HIGH (canary missing → RCE) |
heap-use-after-free | UAF | HIGH — check if write capability |
global-buffer-overflow | OOB on global | Context-dependent |
null-dereference | NULL deref | LOW-MEDIUM (usually DoS) |
double-free | Double free | HIGH in glibc < 2.35 |
UBSan signed-integer-overflow | Integer overflow | Context-dependent |
UBSan shift-exponent | Undefined shift | Usually LOW |
Assess each unique crash:
addr in the ASan report
correlates with bytes from the fuzz input.checksec --file=/workspace/build-afl/harness.
No canary on a stack overflow = trivially exploitable.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 -20For each unique crash, preserve:
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.
| Tool | Install | Use |
|---|---|---|
afl-fuzz | apt install afl++ | Coverage-guided mutation fuzzer |
afl-clang-fast | bundled with afl++ | Instrumented compilation |
afl-tmin | bundled | Test case minimization |
afl-cmin | bundled | Corpus minimization |
afl-whatsup | bundled | Multi-instance status summary |
| libFuzzer | clang built-in | In-process fuzzer (LLVM) |
cargo fuzz | cargo install cargo-fuzz | Rust fuzzing (libFuzzer backend) |
PurpleAILAB/Decepticon
49888ee
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.