You are on page 1of 10

In-Process Fuzzing With Frida about:reader?url=https://bananamafia.

dev/post/frida-fuzz/

bananamafia.dev

In-Process Fuzzing With Frida


9-11 minutes

In a previous post I’ve already covered Frida and its


instrumentation abilities. But check this out: You can also use Frida
to perform fuzzing. What’s even greater is that Frida allows in-
process fuzzing.

Why would you want to do this? There may be various reasons, but
the most outstanding one for me is building a fuzzing harness for
closed source applications and libraries. Just take Counter Strike
GO as an example:

If you want to fuzz the map loading routines with maximum speed,
you’d (ideally) want to create a minimized environment that only
performs the map loading. Now the CS:GO client is a graphical
application that performs all kinds of stuff when being launched and
it’s not scriptable to an extent that allows efficient fuzzing. To avoid
executing all this code that’s not even related to map loading, a
fuzzing harness is required. A great example of such a harness for
CS:GO can be found here - the harness consists of a custom
wrapper and some patches.

This of course allows very specific fuzzing at a great speed, but


also requires quite some effort to pull of. An approach that might be
easier to pull involves using Frida and in-process fuzzing. This, of
course, comes with a much higher fuzzing performance impact
compared to the native approach.

Let’s Consider this very sophisticated fuzzing target:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void lol(char *b)

1 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

{
char buffer[1337];
strcpy(buffer, b);
}

int main(int argc, char **argv)


{
lol(argv[1]);
}

Yes, the vulnerability is obvious and yes, you can find it using
simple reverse engineering but I don’t want to build a better PoC
just for people to stop complaining, okay? :)

The idea is to inject some code into the target process and use it to
perform the fuzzing directly in the process as soon as the target
function would be called regularly. This causes the target to
execute all setup routines for us, up to the point where the harness
would also hand over the work to the fuzzer. The target function will
then be called in an infinite loop, with some optional cleanup in
between, while mutating the input. This mutation can also happen
directly in memory, if applicable.

I’ve created this highly complex fuzzing setup using Frida’s Python
bindings:

import frida
import time
import sys

def on_message(message, data):


print(message)

js = """

// Maximum payload size


var size = 2000;

2 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

// Argument for the fuzzed function


var arg = Memory.alloc(size);
var fuzzData = [0x41];

var lolAddr = null;


var lolHandle = null;

// Find the vulnerable function in the target


process
// and get a handle to it
Module.enumerateSymbolsSync("yolo").forEach(function(symbol){
switch (symbol.name) {
case "lol":
lolAddr = symbol.address;
// use the function prototype to
create a handle
lolHandle = new
NativeFunction(ptr(lolAddr), "void", ["pointer"]);
console.log("[i] lol() is at " +
lolAddr);
}
});

if (lolAddr == null) {
die("Error finding symbol");
}

// Fuzz the function in-process


Interceptor.attach(ptr(lolAddr), {
// Begin fuzzing as soon as the application
calls the function itself
onEnter: function(args) {
console.log("[i] Original argument: " +
args[0].readCString());

console.log("[*] Fuzzing now");


while(fuzzData.length < size) {

3 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

fuzzData.push(0x41);
Memory.writeByteArray(arg, fuzzData);
try {
lolHandle(arg);
}
catch(e) {
console.log("[!] Crash found for
size " + fuzzData.length);
break;
}
}
},
});
"""

pid = frida.spawn(["./yolo", "hello"])


session = frida.attach(pid)

script = session.create_script(js)
script.on('message', on_message)
script.load()

frida.resume(pid)

sys.stdin.read()

Ok cool, what does this all do? Here’s the output:

[i] lol() is at 0x5560a8bc5149


[i] Original argument: hello
[*] Fuzzing now
*** stack smashing detected ***: <unknown>
terminated
[!] Crash found for size 1353

The script causes Frida to spawn the process, inject the JavaScript

4 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

code into it and resume the target afterwards. The target process
will then proceed with its normal execution up to the point where
lol() gets called. The JavaScript code defines a hook for this
function that hijacks the program flow and causes the target to fuzz
itself using mutated input. This mutation happens directly in
memory using a buffer that Frida allocated for itself. This mutation
can do all kinds of things, for the example it’s enough to simply
grow the string length over time until the target crashes.

For a real-world fuzzing setup you would use


Process.enumerateModulesSync() and
Module.enumerateSymbols("<module>") to get the
addresses of the target functions in order to hijack the program
flow. The Frida hooks would then allow mutating the input data and
calling the fuzzed function.

Luckily @dennismantz and @lod108 have released Frizzer


recently! It’s a coverage-guided blackbox fuzzer based on the Frida
instrumentation framework. It’s still a work in progress project but I
think it’s in a very useful state that can be adapted to own projects
pretty quickly at this point.

The best feature is that it uses coverage information provided by


Frida/Stalker in order to find new execution paths while fuzzing and
mutating the input accordingly. This mutation is based on a pre-
defined set of corpus files.

Frizzer Example

You can find some fuzzing targets in the Frizzer repository, but I’ve
built one myself in order to see how Frizzer performs and what it
expects. I’m using a target modified from my previous stack canary
post:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>

5 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 7777

void thisIsBad()
{
char yolo[12];
strcpy(yolo,
"13333333333333333333333333333333333333333333333333337");
}

void pwned(char *buf)


{
printf("[*] Received: %s\n", buf);
if (strlen(buf) >= 16)
{
printf("1\n");
if (strncmp(buf + 2, "A", 1) == 0)
{
printf("2\n");
if (strncmp(buf + 4, "X", 1) == 0)
{
printf("3\n");
printf("[!] PWNED\n");
thisIsBad();
}
}
}
}

int main()
{
int sockfd, ret;
struct sockaddr_in serverAddr;

6 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

int newSocket;
struct sockaddr_in newAddr;

socklen_t addr_size;

char buffer[1024];
pid_t childpid;

sockfd = socket(AF_INET, SOCK_STREAM, 0);


memset(&serverAddr, '\0', sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr =
inet_addr("127.0.0.1");

ret = bind(sockfd, (struct sockaddr


*)&serverAddr, sizeof(serverAddr));
if (listen(sockfd, 10) == 0)
{
printf("[+] Listening....\n");
}
else
{
printf("[-] Error Binding to Port\n");
}

while (1)
{
newSocket = accept(sockfd, (struct
sockaddr *)&newAddr, &addr_size);
printf("[*] Got Connection from %s:%d\n",
inet_ntoa(newAddr.sin_addr),
ntohs(newAddr.sin_port));

char data[32];

7 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

bzero(data, sizeof(data));
recv(newSocket, data, 32, 0);

if (!strlen(data))
{
continue;
}

pwned(data);

send(newSocket, "OK\n", strlen("OK\n"),


0);
close(newSocket);
}

return 0;
}

This is a simple server application that accepts user input via TCP.
This was compiled using gcc -no-pie test.c -o test.
Disabling PIE theoretically isn’t necessary because Frizzer could
get the address of the function to fuzz directly on run time, as seen
in the first example at the beginning of this post.

This is the script used to start the fuzzing process:

#!/bin/bash

rm -rf tmpprojdir

frizzer init tmpprojdir


cat > tmpprojdir/config <<EOF
[fuzzer]
log_level = 3
debug_mode = false

[target]
process_name = "test"

8 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

function = 0x00401294
remote_frida = false
fuzz_in_process = true
modules = [
"/<path to target>/yolotest/test",
]
EOF

frizzer add -p tmpprojdir indir


frizzer fuzz -p tmpprojdir

As can be seen, Frizzer requires the address of the target function


that’s going to be fuzzed. I’ve determined this address using
radare2 like this:

$ r2 -A -c "afl~pwned" -q test
0x00401294 5 179 sym.pwned

Also, Frizzer seems to mutate the first parameter of the target


function during fuzzing - in this case a char* value. If you find
yourself trying to fuzz something different you might have to
customize Frizzer to your needs :)

The directory indir will be filled with files that contain corpus data.
This data is the basis for all the fuzzing that’s going on.

Let’s see Frizzer in action:

9 of 10 25-Feb-20, 12:12 PM
In-Process Fuzzing With Frida about:reader?url=https://bananamafia.dev/post/frida-fuzz/

And that’s it :)

As you can see, fuzzing using Frida seems to be a promising and


quite interesting approach. I’m quite interested to see how Frizzer
evolves and what kind of vulnerabilities it’s going to find.

K THX BYE

10 of 10 25-Feb-20, 12:12 PM

You might also like