- [Instructor] So what a mutation-based fuzzer,
I think it's fairly obvious,
is it takes well-formatted input,
like a message, a string, maybe a file,
and it mutates the data.
And you don't need to have any structure of the input data
for a mutation-based fuzzer, at least for very simple ones.
But for good code coverage, you need that
large collection of well-formed inputs.
This is called a Corpus.
And you can get this information from the internet
when you're fuzzing against a file,
or maybe you're doing fuzzing
against a certain packet format.
You can get just p caps that are from the internet
that are basically representations of the data,
that the process would use,
and then you could mutate that data
and use it as fuzzing test cases.
And those mutations can include flipping bits,
you can duplicate certain fields,
you can randomize data that's in the message,
and then you could use your imagination.
There's many different ways you can mutate the data.
And then the result of the mutation
is to be fed into the target,
and that is your anomalous message.
And then when you have a failure,
you have to have some ways of detecting that failure,
because sometimes, you won't have a hard crash.
Oh maybe, the process is alive, maybe it's stuck,
maybe it is stuck in an infinite loop,
maybe it no longer processes messages,
but the process is waiting for something, basically,
waiting for resource to be freed up.
So, you have to have some way of monitoring this.
And typically, you would monitor the process directly,
and you'd just see whether or not the process is running.
Or you can see whether or not it's consuming CPU.
If it consumes over 90% or 95% CPU, that would be indicative
of an infinite loop scenario.
And then another way to detect failures,
which I would recommend,
is just to send a good message,
rather than sending an anomalous message with modifications
to it, you could just send one that's good.
And if the process acts the way it should,
and responds maybe the way it should,
then you that it's fine, you can move on.
And then if you don't have a response,
then you might have a denial of service condition.
Maybe it doesn't crash anything,
maybe it just went off to lunch.
So in the case of a crash, you have to restart the process,
and then note that information.
And then, the anomalous message and program crash
information you saved,
like I said before,
and then you would use that for further analysis.
Generation-based Fuzzers.
These are your Smart Fuzzers.
And those work with a data model of the protocol.
And that data model has to be created in advance.
So, it requires a lot of hard work,
first, before you can ever do any kind of fuzzing.
Generation-based Fuzzers are
typically something you would do much later on,
after you set up mutation for a little while,
you can do some generation fuzzing,
after you have a model.
Can actually create a message from scratch.
In other words, you're actually creating from the ground up,
a message based on the model.
And then, other times, you can just start from the corpus,
and use the model to modify the corpus,
but also do those fix-ups that you need,
to make the protocol look the way it should,
so the parts surpasses the message.
And then, modifications you add to the data model,
for each anomalous test case.
So you can start from one field,
and you can go to another field,
and then, the data model may include fix-ups,
that you have to do,
and those could be intelligently made by the fuzzer,
based on the data model itself.
Any those could be things like length fields,
for instance, or check sounds.
And, well, I found that Generation-based fuzzers,
do provide better results for code.
It maybe is not as well mature.
If the code has been around for a long time,
you may find that Generation fuzzers
may not find anything.
It's kind of that, you really won't know, until you try.
So to move on, on the newer generations of fuzzers,
the Evolutionary Fuzzers.
This was where a lot of research is happening.
And these actually generate test cases based on
looking at the program itself,
so you have another process that run alongside the program,
and it actually watches what's happening with the program,
so you can see what the code coverage is.
You usually start with something very small,
a very small seed corpus, to get you into that state
to where you can start walking through the program.
And then figure out what you want to do from there.
And it aims for a more complete coverage,
and you also want to eliminate redundant test cases,
that are testing the exact same code.
There's really no point, to running the same code,
over and over again, just to look for those
anomalous test cases.
So it's much quicker to set up,
it may give you better results than
both either mutation, or generation-based fuzzing.
But there are some downsides to running with
an Evolutionary fuzzer.
In some cases, the program has to be written a certain way.
It has to be written in a certain language,
it has to be, you have a source code available,
so you can re-compile the program,
so that you could instrument it,
and actually listen to what's going on in the program.
And so, you may need code available.
And you may not have that code available,
so maybe that's something you can't do.
There are Evolutionary fuzzers that do work
on like QEMU.
One of the examples is an American Fuzzy Lop.
It's an experimental feature, but you can try it.
But this is definitely something to try,
simply, like I said, you really wanna always be fuzzing,
when you have a target.
And Evolutionary fuzzer gives you
a quick way to get started.