fuzzing
SKILL.md
Fuzzing
Guide for security developers designing, implementing, and debugging fuzzers.
Decision Tree
What do you need?
│
├─ Design new fuzzer
│ ├─ Simple C/C++ target → LibFuzzer (fastest setup)
│ ├─ Binary-only target → AFL++ QEMU/Frida mode
│ ├─ Custom components needed → LibAFL (Rust, modular)
│ └─ Structured input (JSON/XML/protocol) → Grammar-based approach
│
├─ Write fuzz target/harness
│ ├─ LibFuzzer → See "LibFuzzer Harness" below
│ ├─ AFL++ → See "AFL++ Harness" below
│ └─ LibAFL → See references/libafl.md
│
├─ Debug fuzzer issues
│ ├─ Coverage not increasing → See "Coverage Issues"
│ ├─ Crashes not detected → See "Crash Detection Issues"
│ ├─ Slow execution → See "Performance Issues"
│ └─ Detailed debugging → See references/debugging.md
│
├─ Analyze crashes
│ ├─ Triage/dedup → See references/debugging.md
│ ├─ Minimize → afl-tmin or -minimize_crash=1
│ └─ Root cause → ASan + debugger
│
└─ Improve mutations
├─ Magic bytes blocking → Enable CmpLog
├─ Structured input → Grammar fuzzing
└─ Custom format → Custom mutator
Quick Reference
LibFuzzer Harness
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size < 4) return 0; // Reject too-small
TargetFunction(Data, Size);
return 0;
}
Compile:
clang -g -O1 -fsanitize=fuzzer,address target.c -o fuzzer
./fuzzer corpus/
AFL++ Harness (Persistent Mode)
#include "afl-fuzz.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;
TargetFunction(buf, len);
}
return 0;
}
Compile and run:
afl-clang-fast -o target target.c
afl-fuzz -i corpus -o out -- ./target
LibAFL Minimal Setup
// Harness
let mut harness = |input: &BytesInput| {
let buf = input.target_bytes();
target_function(buf.as_slice());
ExitKind::Ok
};
// Coverage map + observer
static mut MAP: [u8; 65536] = [0; 65536];
let observer = unsafe { StdMapObserver::new("cov", &mut MAP) };
// Feedback: novel coverage = interesting
let mut feedback = MaxMapFeedback::tracking(&observer, true, false);
// Objective: crashes = solutions
let mut objective = CrashFeedback::new();
// Assemble fuzzer
let mut state = StdState::new(rand, corpus, solutions, &mut feedback, &mut objective)?;
let scheduler = QueueScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mutator = StdScheduledMutator::new(havoc_mutations());
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
Debugging Common Issues
Coverage Not Increasing
-
Check instrumentation exists
# AFL++ afl-showmap -o /dev/null -- ./target < input # LibFuzzer ./fuzzer -print_coverage=1 corpus/ -runs=1 -
Input rejected early?
- Seed corpus invalid
- Magic bytes not discovered → Enable CmpLog
AFL_LLVM_CMPLOG=1 afl-clang-fast -o target_cmplog target.c afl-fuzz -c ./target_cmplog -l 2 -i corpus -o out -- ./target @@ -
Harness not reaching target code
- Add debug logging
- Verify input passed correctly
Crashes Not Detected
-
Sanitizers enabled?
clang -fsanitize=address,undefined ... -
Check crash feedback configured (LibAFL)
let mut objective = CrashFeedback::new(); -
Signal handling correct?
- Executor must catch SIGSEGV, SIGABRT
Performance Issues
- Use persistent mode (10-100x faster)
- Move initialization outside loop
// LibFuzzer extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { SlowInit(); return 0; } - Reduce max input size:
-max_len=1024 - Profile:
perf record ./fuzzer -runs=10000
Mutation Strategy Selection
| Scenario | Strategy |
|---|---|
| Binary blob | Havoc (default) |
| Text protocol | Dictionary + havoc |
| Checksum validation | Patch out or custom mutator |
Magic bytes (e.g., %PDF) |
CmpLog / LAF-Intel |
| Structured (JSON/XML) | Grammar-based |
| Complex format | Structure-aware custom mutator |
Enable dictionary:
# AFL++
afl-fuzz -x protocol.dict ...
# LibFuzzer
./fuzzer -dict=protocol.dict corpus/
Enable comparison logging:
# AFL++
AFL_LLVM_CMPLOG=1 afl-clang-fast -o target_cmplog target.c
afl-fuzz -c ./target_cmplog -l 2 ...
Reference Documentation
| Topic | File | When to Read |
|---|---|---|
| LibAFL components | references/libafl.md | Building custom fuzzer in Rust |
| LibFuzzer patterns | references/libfuzzer.md | Simple C/C++ fuzzing |
| AFL++ modes | references/aflpp.md | Coverage-guided or binary fuzzing |
| Mutation strategies | references/mutations.md | Improving mutation effectiveness |
| Grammar fuzzing | references/grammars.md | Structured input formats |
| Debugging & triage | references/debugging.md | Diagnosing issues, analyzing crashes |
Choosing a Framework
| Requirement | Recommendation |
|---|---|
| Fastest setup | LibFuzzer |
| Binary-only target | AFL++ (QEMU/Frida) |
| Maximum customization | LibAFL |
| Parallel scaling | LibAFL (LLMP) or AFL++ (-M/-S) |
| Structure-aware | LibAFL + Gramatron, or libprotobuf-mutator |
| CI integration | LibFuzzer (OSS-Fuzz compatible) |
Weekly Installs
1
Repository
eyh0602/skillshubGitHub Stars
4
First Seen
6 days ago
Security Audits
Installed on
zencoder1
amp1
cline1
openclaw1
opencode1
cursor1