Fuzzing and crash triage
Stop reading for the bug and make the crash come to you - then work out which crashes are the same bug and which one is exploitable.
Assumes24. Binary exploitation
By the end you can
- Write a harness that reaches the interesting code in as few layers as possible
- Build a seed corpus and say why each seed is there
- Explain what coverage instrumentation buys, and what it costs on a slow target
- Turn a sanitiser report into a one-line statement of the bug's class and location
- De-duplicate crashes by root cause rather than by stack hash
- Judge exploitability from the primitive rather than from the crash message
1. Read
Coverage-guided fuzzing: making the crash come to you
When a pwn or rev challenge hands you a parser and asks for the bug, you can read every line - or you can let AFL++ find the crash while you sleep. Harnesses, corpus, sanitizers, and what to do when the fuzzer gets stuck on a checksum.
Integer bugs: overflow, signedness, truncation, and the off-by-one
The length check that passes because the length wrapped, the negative index that survives a bounds check, and the 16-bit truncation that turns 65,540 into 4. Where arithmetic bugs come from and how to spot them in a decompiler.
2. Use the tools
In the order they come up while solving. Read what each one does, or go straight to the workspace tab that runs it.
- ELF, PE and Mach-O binary analyzer
Parse headers, sections, imports, and symbols from Linux, Windows, and macOS binaries, with C++ symbol demangling and gadget discovery.
Open it in the workspace - Cyclic pattern generator and offset finder
Generate a de Bruijn sequence and find the exact overflow offset from a crashed register value - the pwntools cyclic workflow, in the browser.
Open it in the workspace - Buffer overflow offset finder
Find the exact number of bytes before the saved return address using a de Bruijn pattern and the value from a single crash.
Open it in the workspace - Struct pack and unpack (p32, p64, u32, u64)
Convert integers to little-endian byte strings and back at 8, 16, 32, and 64 bits - the pwntools p32/p64 helpers without the install.
Open it in the workspace - Pwntools exploit script generator
Generate a working pwntools template with the right context, process or remote connection, and the boilerplate every exploit repeats.
Open it in the workspace
3. Try one now
Generated in your browser and checked in your browser. No account, nothing to download, and a fresh one whenever you want another.
4. Practise on the real thing
Real picoCTF challenges that use these techniques, easiest first. 21 match in total - see the full index.
Common mistakes
The wrong turns this topic reliably produces. Written as the mistake rather than the rule, because the rule is easy to agree with and easy to walk straight past.
- Fuzzing the whole program through its normal entry point. Most of the run is spent in argument parsing and setup; the harness exists to skip that.
- Counting crashes. A thousand crashes are usually a handful of bugs, and until they are de-duplicated the number means nothing.
- Treating a sanitiser report as an exploit. Heap-buffer-overflow READ of size 1 and WRITE of size 8 are very different findings.
- Judging a corpus by how many files are in it. A thousand near-identical seeds explore one path; a corpus is doing its job when each seed reaches code the others do not, which is a coverage measurement rather than a count.
- Fuzzing a target built without sanitisers. Corruption that does not happen to crash passes unnoticed, so the run finds the loud bugs and silently misses the class this module is about.
Checkpoint
Take a small parsing target, produce a harness and a seed corpus, run it to a crash, and hand in the minimised input plus a one-sentence root cause.
Teaching note
The lesson students resist is that harness quality dominates everything else. Show two runs on the same target - one through the CLI, one through a direct call into the parser - and let the coverage numbers make the argument.
Go deeper
The lessons above are written to get you through a challenge. These are the chapters to read when you want the subject instead - each one named so you can check it rather than take our word for it. Nothing here is affiliate-linked or sold by us.
From Day Zero to Zero Day - Eugene Lim
Chapter 7, Quick and Dirty Fuzzing
The cheapest useful fuzzer, which is the one that should exist before any sophisticated one does.
From Day Zero to Zero Day - Eugene Lim
Chapter 8, Coverage-Guided Fuzzing
Instrumentation, corpora and scheduling explained in the order you actually need them.
From Day Zero to Zero Day - Eugene Lim
Chapter 3, Automated Variant Analysis
What to do after the first bug: the same mistake is usually present three more times.
Practical Binary Analysis - Dennis Andriesse
Chapter 12, Principles of Symbolic Execution
The other way to reach deep code, and an honest account of where it stops scaling.
Bug Bounty Bootcamp - Vickie Li
Chapter 25, Automatic Vulnerability Discovery Using Fuzzers
The same technique aimed at web targets, where the oracle is a response rather than a signal.