| Crates.io | peacock-fuzz |
| lib.rs | peacock-fuzz |
| version | 0.2.4 |
| created_at | 2023-12-16 14:17:31.318073+00 |
| updated_at | 2024-07-19 14:12:30.554837+00 |
| description | Library to parse context-free grammars and create grammar-based fuzzing tools |
| homepage | |
| repository | https://github.com/z2-2z/peacock |
| max_upload_size | |
| id | 1071746 |
| size | 171,768 |
~~~ fuzzing with grammar-based mutations ~~~
This project is a reimplementation of Gramatron that is
Clone the repo and execute
cargo build --release
This creates 5 ready-to-use tools:
peacock-fuzz: A coverage-guided fuzzer that can fuzz any binary compiled with AFL++'s compilers or anything that speaks AFL's forkserver protocolpeacock-dump: peacock-fuzz saves crashes and queue items in a raw, binary format to disk. Use this tool to get a human readable output from any such file. All these binary files have the prefix peacock-raw-peacock-compile: Takes a grammar and compiles it to C codepeacock-merge: Merge multiple grammar files into one or convert a grammar file from one format into anotherpeacock-gen: Generate individual inputs from a grammarIf you want more fine-grained control you can use the crate peacock_fuzz, which is the backbone of all the tools from above.
See the documentation at docs.rs in order to get started with peacock as a library.
Peacock is a fuzzer that implements so-called "grammar-based mutations". This means that it will mutate its inputs in such a way that they will always adhere to a given grammar.
The way mutations work is the same as in Gramatron. A grammar is converted to a PDA such that an input can be represented as a walk through the automaton. Then, a mutation of an input is simply a modification of an automaton walk. We cut off the walk at a random point and let it find a new random path through the automaton from there.
While Gramatron and LibAFL realize the automaton as an adjacency matrix, peacock generates C code that encodes the automaton in its control flow. This saves us a lot of memory accesses and makes the mutation procedure faster.
The generated C code exposes a certain API that can be used by any application, e.g. a libfuzzer harness, an AFL++ custom mutator or even Rust code.
Peacock also ships a ready to use fuzzer that can fuzz any binary that has been compiled with AFL++'s compilers or implements an AFL-style forkserver.
Peacock accepts its context-free grammars in JSON format. A context-free grammar has production rules of the form:
A -> X Y Z ...
where A must be a non-terminal and X,Y,Z can be non-terminals or terminals. The right-hand-side must contain at least one symbol.
Non-terminals are enclosed in <>, so the non-terminal A would be represented as <A>. Terminals are enclosed in ''.
The set of rules
A -> a B
A -> a
B -> b B
B -> Ɛ
would be written as
{
// Comments are also possible :)
"<A>": [
["'a'", "<B>"],
["'a'"]
],
"<B>": [
["'b'", "<B>"],
["''"] // Ɛ = ''
]
}
and corresponds to the regular expression a(b*).
Peacock also supports the Gramatron format, which is a bit different and does not allow for comments.
The non-terminal <ENTRYPOINT> is the entrypoint of the grammar.
void seed_generator (size_t new_seed)
Supply a seed for the RNG of the mutator.
size_t unparse_sequence (size_t* seq_buf, size_t seq_capacity, unsigned char* input, size_t input_len)
Given an input that adheres to the grammar, find the corresponding automaton walk. This function may be slow, use outside of hot loop.
seq_buf: Automaton walk will be written into this bufferseq_capacity: Maximum number of elements that seq_buf can hold (not number of bytes)input: User input adhering to grammarinput_len: Length of inputReturns the number of elements written to seq_buf or 0 if input does not adhere to grammar.
size_t mutate_sequence (size_t* buf, size_t len, size_t capacity)
Given an automaton walk, create a random mutant of the walk.
buf: Pointer to array that holds automaton walklen: Number of items in buf (not number of bytes)capacity: Maximum number of items that buf can hold (not number of bytes)Returns the length of the new walk.
size_t serialize_sequence (size_t* seq, size_t seq_len, unsigned char* out, size_t out_len)
Given an automaton walk, create the corresponding output.
seq: Pointer to automaton walkseq_len: Number of items in seq (not number of bytes)out: Output will be written into that bufferout_len: Number of bytes in outReturns how many bytes have been written to out.
Macros:
MAKE_THREAD_SAFE: Define this to make the mutator completely thread-safeMAKE_VISIBLE: Define this to explicitly set the visibility of the functions from above to "default"STATIC_SEED=<your seed>: Compile-time seed for the RNGDISABLE_rand: Don't include the internal rand function and use an external one with the signature size_t rand (void)DISABLE_seed_generator: Don't include the function seed_generator