Friday 9 September 2016

YOLOFuzZ

#showerthoughts

A fuzzer that flips jump instructions in a program to force greater code coverage.


Is this a shit idea?
Probably, but lets think about it anyway

For each crash we'd produce a stack trace and a trace of jmp instructions noting which ones were changed.


Initial thoughts are that there are going to be lots of crashes. Each crash is going to take a fair bit of effort to work out if it's worth investigating.


OPTIMISATION?

Yeah ok, so I could sort the gazillion crashes by "depth" and "amount changed". Prioritising crashes that got deep into the code and required the fewest changes.

I'll have to compile the program with all those 'unroll loop' options in gcc, convert shit to memcpy's if I can and probably ignore jmps in library calls.


Ok so now this is actually sounding like it might work.


Analysis stage:

When I find a nice deep crash that only has a couple of jump flips the next stage is to find out how to change the input to legitimately hit that crash.


1) Analyse the compare instruction before the jump.
    - Back trace and find the input bytes being compared
    - Add the byte/strcmp string to a list of fuzz shit

2) Fuzz the input till it trips the jmp.



Fuzz Mapp:
Fuzz small chunks (or even individual bytes) to find the ones that have no effect on the code path and note the values required to get our current code path from the ones that do.


FuzzFlip:

Get the bytes/strings compared around jmp instructions and add those to our bucket of things to chose from when fuzzing.




The more that I think about this the more I'm thinking that it would be a beast of a fuzzer.


Example:

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
 
 if (strcmp(argv[1], "MAGIC_OMG_SECRET_KEY")){
  // Super cool code segment
 }

 return 0;
}



Most fuzzers would get fuzked over by something like this. But YOLOFuzZ™ would be like "Yolo! Let's just do this anyway!XD!" and it'd go have a fun time.



Thinking about it seriously

Now that I think about, instead of aiming for crashes, it could simply aim for code coverage. Really a program can kinda be thought of as a graph where nodes are code segments and jmps are like edges.

Here's what I'm thinking.

I run the fuzzer and for every block of code it hits it saves the trail jmps modified to get there. If there is already a path that gets there then continue from the one that uses fewer changed jumps. Essentially using Dijkstra's algorithm to find code coverage.

Once it's got 100% coverage, take the most commonly needed flipped jmps and start trying to find legit ways to hit them. I'll probably want to hang on to all the different ways to hit a function incase the shortest one doesn't work.

To find valid ways of hitting the jumps, analyse the comparison instructions that precede the jump. We tackle this part in two directions, from our input and from the program. Find our input and find what it's comparing it against (I know it's not that simple but a lot of cases fall under this).

- Search for any matches that occur in our input, change them, run again and see if they reach the comparison. 
- Backtrace and mark out regions of memory, look what goes there, where does that come from etc..
- Look for string comparisons, constants, global variables etc.
Fuzz all of the above until we get past our jump. 
Continue adding to the possible paths in the program.

At this point we have a bazillion different ways to reach various code segments and we'll undoubtably have a bunch of crashes. This gives us heaps of attack vectors and interesting spots to attack. Pick somewhere that looks interesting and start applying some human-smarts into changing the input so that it can hit that target.

You could even apply this in reverse. Find an interesting code segment and then point YOLOFuzZ at it and you can then find a way to tweak the input to get there.

Actually implementing this would be a fairly big task. I reckon I could get something pretty simple working if I put a few weekends to it. The backtracing etc would be the hardest that has like infinite scope.

I'd make it as modular as possible and have it so that I could easily add an analysis module that dumps where the jumps hit. That way, the module can return possible input that will trip the jump. Such flexible :)

Another trek of a job is the testing harness, that's a super crucial part of any fuzzer.

Another Idea for a fuzzer. Generate all possible inputs! Every if statement, fork off and go down both, generating the required input as needed mwahahaha. Ok that's enough fuzzy thinking for now, I'll save analysis of that idea for another day. 


PS: as a bonus, this fuzzer would also crack a lot of software