Boofuzz – A helpful guide (OSCE – CTP)

Overview

Whilst studying for the Cracking the Perimeter you will come across many references to the Spike fuzzer. This is the fuzzer of choice for the CTP course. However development for Spike has long since ceased.

Enter Sulley, the Spike fuzzer replacement.

And then exit Sulley, as it has not seen active development in 2 years (as of November 2018).

Enter Boofuzz! the successor to Sulley and topic of today’s post.

Boofuzz is still actively maintained and is a great choice if you wish to go beyond the CTP course information and work with a more modern fuzzer.

Python is the language of choice for Boofuzz, and as a result it is quite powerful. However, if you google around you wont find tens of thousands of search results like you would if you searched for help with spike.

41,400 results for spike
466 results for Boofuzz

 

Thankfully, ALOT of those 466 results for Boofuzz are of jtpereyda answering lots of different questions from users. He has answered questions in other languages to help all kinds of Boofuzz users out.

Hopefully this post can help consolidate some of that information into a single source with various examples.

In this blog post we are going to grab Boofuzz and Vulnserver, and learn as we go. Developing template scripts to crash a bunch of vulnservers vulnerabilities and enhancing our fuzzing script with each crash.

The Setup

Installing our software

If you wish to participate rather than just reading along we will need a few things to get started.

First we need a windows machine to run vulnserver and Boofuzz from (I am using the same machine for both). I chose to spin up a Windows 7 VM.

Second we need to download vulnserver.

Third we need to download Boofuzz. To download Boofuzz (and use it) we’ll need python. So go ahead and download and install python 2.7 (NOT 3.x) onto your windows machine.

On my Windows 7 Machine I also needed to download the Microsoft C++ compiler for python 2.7  for Boofuzz to install correctly.

With python and the C++ compiler installed we can then install boofuzz with pip.

We’re nearly there, we want to be able to manually connect to vulnserver to play around and gather information for later fuzzing.
For this we’ll grab netcat.

Finally we need a debugger to monitor for crashes. Let’s grab Ollydbg and drop it into our windows VM along with our Vulnserver and netcat(nmap) downloads.

Run the installer for Nmap to get access to the Ncat binary. There’s no need for anything fancy, just next next finish.

Running our software

To get started we need to get vulnserver up and running. Thankfully this is as simple as running vulnserver.exe from the command line.

By default vulnserver will run on port 9999. This can be changed by adding a port number as an argument when calling vulnserver.

Alright, with vulnserver running lets attach our debugger to the process.
Here’s quick video for anyone that struggles with that.

Setup complete!

Intro to Boofuzz

The session boilerplate

This information exists in plenty already but for the sake of completeness lets cover a few basic Boofuzz concepts.
The first concept is the Session object.
You’ll use the session object every time you create a fuzz script. You will declare connections with session.connect(xxxx), you will send fuzz payloads with session.fuzz() and you will define session callbacks to interact with your protocol of choice.
And this is how you will build your session object:

session = Session(
    target=Target(
        connection=SocketConnection("127.0.0.1", 8021, proto='tcp')))

That’s straight from the doco so nothing crazy here. Let’s break it down a bit.
Line 3 – We define a connection object and pass it our target IP address, target port and target protocol.
Line 2 – The connection object is passed as the Target for a target object.
Line 1 – The target object is used to create a session object, which can now be used to establish connections and fuzz our target.
There are more arguments that can be passed to each object, but we’ll touch on them as we need them to reduce complexity now.

 

Requests and Blocks

Boofuzz makes use of the concept of requests and blocks when creating a fuzzing template.
Requests begin with s_initialize(‘requestname’).
Blocks (or primitives) that follow a request declaration form part of that request.

Lets make that clearer by using the documentation example once again (don’t worry we’ll cover non doco examples soon).

Connections

Alright, one last piece before we can fuzz.
We need to tell let Boofuzz know how we are going to use each request.
This looks something like so:

That’s it!
well.. add a session.fuzz() and that’s it.

The last thing I’ll do for this intro is drop in the quickstart Boofuzz script to put it all together.
It should make sense to you after having read this introduction.

#!/usr/bin/env python
# Designed for use with boofuzz v0.0.8
from boofuzz import *


def main():
    """
    This example is a very simple FTP fuzzer. It uses no process monitory
    (procmon) and assumes that the FTP server is already running.
    """
    session = Session(
        target=Target(
            connection=SocketConnection("127.0.0.1", 21, proto='tcp')))

    s_initialize("user")
    s_string("USER")
    s_delim(" ")
    s_string("anonymous")
    s_static("\r\n")

    s_initialize("pass")
    s_string("PASS")
    s_delim(" ")
    s_string("james")
    s_static("\r\n")

    s_initialize("stor")
    s_string("STOR")
    s_delim(" ")
    s_string("AAAA")
    s_static("\r\n")

    s_initialize("retr")
    s_string("RETR")
    s_delim(" ")
    s_string("AAAA")
    s_static("\r\n")

    session.connect(s_get("user"))
    session.connect(s_get("user"), s_get("pass"))
    session.connect(s_get("pass"), s_get("stor"))
    session.connect(s_get("pass"), s_get("retr"))

    session.fuzz()


if __name__ == "__main__":
    main()

Ok. Let’s move on to funner things.

 

Vulnserver

Figuring out the request structure

Determining the request structure is the first step before doing any fuzzing. You might do things such as reading the protocol RFC, watch the connection stream in wireshark, and/or simply connect to the application and determine what data to send.

Vulnserver is extremely simple in this regard and we will be sticking with the last approach.

Lets see how that looks.

 

Connect to the remote application. Netcat is good enough for this scenario.

 

Already we have some information. Upon connection this application will display a banner “Welcome to Vulnerable Server! …”.

Also this lets us know we have a help menu which will allow us to gather more information.

 

 

Cool. Lets test one of these commands.

Alright, so we have a bucket load of commands to fuzz and we know how to send them.

<command><space><value>

That’s enough information gathering for now. Let’s jump into fuzzing our first target command.

 

Fuzzing – KSTET

 

Our first choice is the KSTET command.

The script we build for this fuzz attempt will be as basic as it gets. It will mostly just show how to replicate the usual Boofuzz examples but for Vulnservers KSTET command rather than FTP. Don’t worry, we’ll progress into better scripts as we go.

Alright, lets piece this first script together in slow time.

from boofuzz import *

def main():

        ## Just some variables to make swapping targets easier
	port = 9999
	host = '127.0.0.1'
	protocol = 'tcp'

        ## First things first, we set up our boilerplate session code
        ## This should look familiar and is no different from the quickstart example above
        ## aside from the use of variables for host, port and protocol.
	session = Session(
			target=Target(
				connection = SocketConnection(host, port, proto=protocol),
			),
	)

if __name__ == "__main__":
	main()

That’s enough to create our session object that we always need.
This is the first step to creating any Boofuzz script and wont vary too greatly between scripts.
The next part is setting up our requests. This section is built differently for every different kind of fuzzing session you might want to do.
lets build our request to fuzz the KSTET command:

	s_initialize("kstet")  ## This begins our request and names it "kstet"
	s_string("KSTET", fuzzable=False) ## The first block of our request. This contains the KSTET command.
	s_delim(" ", fuzzable=False) ## The next block of our request contains the space that is needed between command and value.
	s_string("FUZZ") ## This block is the most interesting. This block represents the value for the command, and is what we will fuzz.
	s_static("\r\n") ## The final block hits enter for us.

Ok, a few new things to explain here.
s_string() lets us declare a string value for our request. By default this will be mutated by the fuzzer. Since we want to send the KSTET command unmolested each time, we set fuzzable to False.
s_delim() is used to define the delimiting character used between 2 blocks. By default this will also be fuzzed.
s_static() is a static value that will not be changed.

Great! so that’s our request sorted. This application doesn’t require authentication or any other steps. So this request is all we need to fuzz the KSTET command.
That means we can just set up the connection with one line and then tell Boofuzz to begin fuzzing the target.
like so:

	session.connect(s_get("kstet"))
	session.fuzz()

Simple stuff here.
We only have one request so our request chain is a single line.
Then we initiate the fuzz.

Here is the full script:

from boofuzz import *

def main():


	port = 9999
	host = '127.0.0.1'
	protocol = 'tcp'

	session = Session(
			target=Target(
				connection = SocketConnection(host, port, proto=protocol),
			),
	)

	s_initialize("kstet")
	s_string("KSTET", fuzzable=False)
	s_delim(" ", fuzzable=False)
	s_string("FUZZ")
	s_static("\r\n")

	session.connect(s_get("kstet"))
	session.fuzz()

if __name__ == "__main__":
	main()

Lets run our debugger script against Vulnserver and see what happens.
Attach vulnserver to a debugger and run your script.

Awesome!
Our first crash.
The fuzzing script continues to run though.. that’s not ideal.
We will have to fix that.

Fuzzing – HTER

Next up we will be fuzzing the HTER command.
We will be making some fairly significant changes to the script here.
Specifically we will be changing two things:

Logging
and
Callbacks

I’ll try to be detailed here.

        Logging

By default, Boofuzz will output its session logs to a SQLite formatted, timestamped database (.db) file.
You can grab whatever your software of choice is to open and interact with the database file.
OR you can create a new logger that will output its results to a nicely formatted CSV file.

I’m going to drop in the updated code first and then explain line by line what has happened afterwards.
Here goes.

	csv_log = open('fuzz_results.csv', 'wb') ## create a csv file
	my_logger = [FuzzLoggerCsv(file_handle=csv_log)] ### create a FuzzLoggerCSV object with the file handle of our csv file


	
	session = Session(
			target=Target(
				connection = SocketConnection(host, port, proto=protocol),
			),
			fuzz_loggers=my_logger, ## set my_logger (csv) as the logger for the session
	)

As you can see,  there aren’t too many changes.
I’ve added two lines (the first two), and added one line to the boilerplate session creating code.

Alright. Let’s start with the first line.

	csv_log = open('fuzz_results.csv', 'wb')

This is not Boofuzz specific stuff, we just open a file as “csv_log” called fuzz_results.csv in write mode.

Next line.

        my_logger = [FuzzLoggerCsv(file_handle=csv_log)]

alright lets step through this one.
Step 1. – Instantiate an object called my_logger
Step 2. – Call FuzzLoggerCsv() with the argument file_handle=csv_log (csv_log is our open .csv file remember). From the doco FuzzLoggerCsv is a class that formats data for pcap files, and can be configured to output to a named file.
Step 3. – Wrap FuzzLoggerCsv() in [ ] since a list is expected later.

And the last line.

			fuzz_loggers=my_logger,

If you look at the documentation for Sessions you will see that one of the allowed parameters is fuzz_loggers. Fuzz_loggers takes a list of loggers for saving test data and results to. This is why my_logger was declared as a list earlier.
This line simply adds my_logger (which uses our .csv file) to the list of loggers being used for this session.

And that is it!
CSV logging is now a thing in our script.

        Callbacks

Next up is the callbacks.
Callbacks are described in the session page of the documentation. If defined, callbacks are called in between transmissions of the current request. This allows for handling situations like dealing with challenge and response systems.
We are going to use callbacks for a different reason.
Callbacks are going to solve our “the fuzzer just keeps running after a crash” problem.
Are they meant to be used this way? probably not.
Does it work? sure does.

Lets jump into it with the code dump.

from boofuzz import *
from sys import exit

def get_banner(target, my_logger, session, *args, **kwargs):
	banner_template = "Welcome to Vulnerable Server! Enter HELP for help."
	try:
		banner = target.recv(10000)
	except:
		print "Unable to connect. Target is down. Exiting."
		exit(1)

	my_logger.log_check('Receiving banner..')
	if banner_template in banner:
		my_logger.log_pass('banner received')
	else:
		my_logger.log_fail('No banner received')
		print "No banner received, exiting.."
		exit(1)

def main():


	port = 9999
	host = '127.0.0.1'
	protocol = 'tcp'
	
	csv_log = open('fuzz_results.csv', 'wb') ## create a csv file
	my_logger = [FuzzLoggerCsv(file_handle=csv_log)] ### create a FuzzLoggerCSV object with the file handle of our csv file


	
	session = Session(
			target=Target(
				connection = SocketConnection(host, port, proto=protocol),
			),
			fuzz_loggers=my_logger, ## set my_logger (csv) as the logger for the session
	)

	s_initialize("hter")
	s_string("HTER", fuzzable=False)
	s_delim(" ", fuzzable=False)
	s_string("FUZZ")
	s_static("\r\n")

	session.connect(s_get("hter"), callback=get_banner)
	session.fuzz()
	




if __name__ == "__main__":
	main()

Ok. That was a bit of a jump in code length.
Lets break it down again.

What has changed in main?
Almost nothing honestly. Just this one addition to the connect() call.

session.connect(s_get("hter"), callback=get_banner)

We just set the callback argument (which usually defaults to none) to our “get_banner()” function. That’s it.
Now our get_banner() function will be called in between transmissions of the source and destination requests.
Cool.

On to the get_banner() function

def get_banner(target, my_logger, session, *args, **kwargs):
	banner_template = "Welcome to Vulnerable Server! Enter HELP for help." ## define how the Vulnserver banner looks
	try:
		banner = target.recv(10000)    ## Attempt to grab the banner from vulnserver and save it as "banner"
	except:
		print "Unable to connect. Target is down. Exiting." ## If that fails, the target is down so we exit and stop the fuzzer
		exit(1)

	my_logger.log_check('Receiving banner..')  ## If we connected and received data we log the check
	if banner_template in banner:   ## check if what we grabbed matches the banner
		my_logger.log_pass('banner received')  ## if its a match we just log the success and hand back to the fuzzer to continue
	else:
		my_logger.log_fail('No banner received') ## if it's not a match we assume something has happened with the app and exit.
		print "No banner received, exiting.."
		exit(1)

The function is constructed as per the requirements defined in the documentation.
More specifically I am referring to the example_test_case_callback() documentation. Which is shown below.

example_test_case_callback(target, fuzz_data_logger, session, *args, **kwargs)

Example call signature for methods given to register_post_test_case_callback().

Parameters:
target (Target) – Target with sock-like interface.
fuzz_data_logger (ifuzz_logger.IFuzzLogger) – Allows logging of test checks and passes/failures. Provided with a test case and test step already opened.
session (Session) – Session object calling post_send. Useful properties include last_send and last_recv.
sock – DEPRECATED Included for backward-compatibility. Same as target.
args – Implementations should include *args and **kwargs for forward-compatibility.
kwargs – Implementations should include *args and **kwargs for forward-compatibility.

That should be enough to explain how the callback is implemented and also the strange way we are using it.

Testing time!

 

        The crash

I’ve uploaded a video for this one. Just to show the instantaneous stopping of the fuzzer.

The video is short and will not open new tabs/windows or any annoying garbage like that.

There we go. That’s two commands fuzzed and we now have a script that will pause on crash and output to CSV.
Let’s try to improve again.

Fuzzing – TRUN

Alright, this is the last fuzzing session for us.
This time we are going to handle crashes the right way.
With process monitoring!

Yet again I’ll dump the entire script and then break it down.

from boofuzz import *

def main():


	port = 9999
	host = '127.0.0.1'
	protocol = 'tcp'
	
	csv_log = open('fuzz_results.csv', 'wb') 
	my_logger = [FuzzLoggerCsv(file_handle=csv_log)] 


	
	session = Session(
			target=Target(
				connection = SocketConnection(host, port, proto=protocol),
                                ## Setup process monitoring
                                procmon=pedrpc.Client(host, 26002),
                                procmon_options = {
                                        "proc_name" : "vulnserver.exe",
                                        "stop_commands" : ['wmic process where (name="vulnserver") delete'],
                                        "start_commands" : ['vulnserver.exe'],
                                }

			),
			fuzz_loggers=my_logger,
                        crash_threshold_element= 20,
	)



	s_initialize("trun")
	s_string("TRUN", fuzzable=False)
	s_delim(" ", fuzzable=False)
	s_string("FUZZ")
	s_static("\r\n")

        
	session.connect(s_get("trun"))
	session.fuzz()


if __name__ == "__main__":
	main()

Not too much new stuff here, lets break it down.

procmon=pedrpc.Client(host, 26002),

check out the arguments you can pass while constructing your target object. One of them is procmon.
This line of code sets that argument to be the process monitor application that is listening on our localhost at port 26002.
Wait.
What process monitor application?
Don’t worry, we’ll get into that after the code breakdown.

The next and last argument is the process monitor options.

procmon_options = {
    "proc_name" : "vulnserver.exe",
    "stop_commands" : ['wmic process where (name="vulnserver") delete'],
    "start_commands" : ['vulnserver.exe'],
}

This provides the process monitor application with the process name to attach to, the command to kill the process, and the command to start the process again.
Just a note, my start command is just “vulnserver.exe”. This is because I dropped vulnserver.exe (and its .dll) into the same folder as the process monitor. If that’s not the case for you, include the full path.
Also, another note. If you’re playing with services, provides the appropriate service start, service stop commands instead of wmic and a binary path.

Ok, last piece of code.

crash_threshold_element= 20,

By default, Boofuzz will stop after 3 crashes. This command changes that to 20 so we can gather more crashes.
You can change it to 1 if you just want to stop after the first crash.

Alright, that’s the script handled but now we need the process monitoring software running.
Boofuzz has you covered there. Boofuzz comes with process_monitor.py in its main directory. However to run it we first need to satisfy some dependencies.
Check out this page of the documentation for what to install.
After you’ve satisfied the dependencies download a copy of Boofuzz and drop it on the desktop of your windows machine.

You should now be able to run process_monitor.py on the windows host.

Now we just need two terminals open. One running process monitor, and the other to run our fuzzing script.
Lets see that in action.

Here’s another short video of the fuzzer working its magic. You’ll notice that a crash occurs almost immediately with EIP being overwritten with 41414141 (AAAA). The process monitor catches this and restarts vulnserver, then Boofuzz continues its fuzzing and finds another crash with 42424242’s this time. This will continue until you either run out of mutations, or hit the crash threshold.

And thats it!
Now you can peruse the .csv log at your leisure checking out different crashes.

Leave a Reply

Your email address will not be published. Required fields are marked *