Fuzzing Vulnserver with Sulley: Part 3

Introduction

Vulnserver is a vulnerable server written by Stephen Bradshaw whose blog is located here: grey-corner. This server was written intentionally to be vulnerable, so we can learn fuzzing on it. It's kinda hard to learn fuzzing if we don't have any existing vulnerabilities in place to test it on.

In part 1, e're already seen that the Vulnserver contains 6 vulnerabilities in different commands. Actually it contains only 4 vulnerabilities in 4 functions: function1, function2, function3 and function4, and the two of the six commands then call the same function. Anyway, the vulnerabilities that can be detected lie in the following Vulnserver commands: KSTET, TRUN, GMON, GTER, HTER and LTER.

The fuzzing process consists of the following fuzzing phases.

Identify Target

We've already done that, since we're about to fuzz the Vulnserver. Therefore the target is already identified and it is the Vulnserver vulnerable server running on port 9999.

Identify Inputs

Before doing anything else, we must identify all input that the application accepts and processes. We know that the vulnerability is present because the application takes a malformed input and processes it without sanitizing. All inputs in the Vulnserver can be identified by connecting to the Vulnserver on port 9999 with telnet and issuing the HELP command, which should display all the commands the Vulnserver accepts. An example of that can be seen below:

# telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Welcome to Vulnerable Server! Enter HELP for help.
HELP
Valid Commands:
HELP
STATS [stat_value]
RTIME [rtime_value]
LTIME [ltime_value]
SRUN [srun_value]
TRUN [trun_value]
GMON [gmon_value]
GDOG [gdog_value]
KSTET [kstet_value]
GTER [gter_value]
HTER [hter_value]
LTER [lter_value]
KSTAN [lstan_value]
EXIT

We can see that the server is indeed listening on port 9999 and once we connected to it, we executed the HELP command that displayed all the valid commands the server supports.

Generate Fuzzed Data

After we've identified all input vectors we must generate the invalid input data to be sent to the targeted application (in our case, the Vulnserver).

Execute Fuzzed Data

When we've generated the fuzzed data, it's time to send it to the Vulnserver on port 9999.

Monitor for Exceptions

We must also monitor the Vulnserver for crashes. When the server crashes we must save the input data that caused the crash for later analysis.

Determine Exploitability

After the fuzzing, we must manually check all saved data that caused the Vulnserver to crash if the Vulnserver can be exploited through the found vulnerability.

Starting the Fuzzing Process

In this step, we'll explain a file vulnserver.py, which will be used as an input to the Sulley fuzzer and will actually perform all the work. To start fuzzing the Vulnserver, we need to start a network_monitor.py and process_monitor.py on a guest operating system - the one that will actually run the program being fuzzed; in our case the Vulnserver. We need to run the following commands on guest operating system (note that the Sulley fuzzer should already be installed and functional):

C:\d C:\sulley\
C:\sulley> mkdir audits\vulnserver\
C:\sulley> python network_monitor.py -d 0 -f "src or dst port 9999" -P audits\vulnserver\
C:\sulley> python process_monitor.py -c audits\vulnserver.crashbin -p vulnserver.exe

We created a new directory that will be used to store the PCAP files - every packet transferred from and to the Vulnserver will be logged in PCAP files. Each PCAP file will contain the information from one iteration of the fuzzing process, so there will be quite a lot of PCAP files. But don't worry, we'll extract only the important ones, we don't need to bother ourselves with the ones that don't crash the server.

On the host operating system we need to start the fuzzing process with the following command, where the vulnserver.py is the input file into the Sulley fuzzing framework that describes all the aspect of a fuzzing process:

# export PYTHONPATH="$PYTHONPATH:/home/user/sulley/"
# python requests/vulnserver.py

We've added the Sulley installation directory to the PYTHONPATH, so the python can find all the libraries that it needs to function properly. After that we've started the actual fuzzing process.

Upon starting the fuzzing process, we can observe the fuzzing progress in a web interface on the URL address http://127.0.0.1:26000. This is a Sulley internal web server that shows us the fuzzing process completeness and the input files that caused a crash. When the process is finished, we should see something like the picture below:

vulnserver0

We can see quite a lot of crashes; the numbers on the left indicate the iteration of the fuzzing process (and therefore also the name of the PCAP file written to the auditsvulnserver directory, where we can find the packets used to crash the vulnerable server). We can see that there are a lot of crashes where the EIP was overwritten with input data, like 0x41414141 or 0x20202020, so those crashes indeed tell us that the input data can be used to execute arbitrary code on the target system.

So far, we've seen the results we should get, but what is actually in the vulnerver.py that does all the fuzzing?

The vulnserver.py Input File

First let's present the whole vulnserver.py file, which looks like the output below:

#!/usr/bin/python
from sulley import *
import sys
import time

""" Receive banner when connecting to server. """
def banner(sock):
  sock.recv(1024)

""" Define data model. """
s_initialize("VulnserverDATA")
s_group("commands", values=['HELP', 'STATS', 'RTIME', 'LTIME', 'SRUN', 'TRUN', 'GMON', 'GDOG', 'KSTET', 'GTER', 'HTER', 'LTER', 'KSTAN', 'EXIT'])
s_block_start("CommandBlock", group="commands")
s_delim(' ')
s_string('fuzz')
s_static('\r\n')
s_block_end()


""" Keep session information if we want to resume at a later point. """
s = sessions.session(session_filename="audits/vulnserver.session")

""" Define state model. """
s.connect(s_get("VulnserverDATA"))

""" Define the target to fuzz. """
target = sessions.target("192.168.1.126", 9999)
target.netmon = pedrpc.client("192.168.1.126", 26001)
target.procmon = pedrpc.client("192.168.1.126", 26002)
target.procmon_options = {
   "proc_name" : "vulnserver.exe",
   "stop_commands" : ['wmic process where (name="vulnserver.exe") delete'],
   "start_commands" : ['C:\\Users\\eleanor\\Desktop\\vulnserver\\vulnserver.exe'],
}


""" grab the banner from the server """
s.pre_send = banner

""" start fuzzing - define target and data """
s.add_target(target)
s.fuzz()

First we're defining a function that will receive the banner upon each connection to the vulnerable server. This is needed, because when initiating the connection to the vulnerable server, the server sends us a welcome message, which we must accept before trying to send any data to the vulnerable server. After the banner function, we're defining a Data Model. A data model is how Sulley describes the input data to be fuzzed. The s_initialize sets the name to the group element. Then we're using the s_group which declares the group where the values parameter specifies all the command names, and the command block follows. The s_block_start and s_block_end specify the starting and ending point of the command body. The command body consists of the each command in turn (as specified in the values parameter to the s_group function) as well as the space delimiter, the string to fuzz and the ending CRLF command termination.

With the s_group, we don't have to define each command separately; we define just the names of the command, followed by a space delimiter and argument, which is to be fuzzed. We've described the declaration of the data model, which doesn't actually do anything just yet. We have to provide a data model to let Sulley know how it should generate data to be sent to Vulnserver when fuzzing each command. After the data model, we need to define a new session, which we can use later in time to continue fuzzing.

What follows is a state model, which describes which states Sulley must go through when fuzzing. In our simple example, we don't need many states, just one that actually uses the previously defined data model and fuzzes its commands as described in the data model. If we would fuzz the FTP server for example, that would be a different story, because we would first need to go into USER state, followed by PASS state, in which we authenticate to the FTP server. Only after authentication we can enter into a state where the fuzzing can really begin.

What follows is the target declaration. Let's take a look at that piece of code again:

target = sessions.target("192.168.1.126", 9999)
target.netmon = pedrpc.client("192.168.1.126", 26001)
target.procmon = pedrpc.client("192.168.1.126", 26002)
target.procmon_options = {
   "proc_name" : "vulnserver.exe",
   "stop_commands" : ['wmic process where (name="vulnserver.exe") delete'],
   "start_commands" : ['C:\\Users\\eleanor\\Desktop\\vulnserver\\vulnserver.exe'],
}

Those lines declare the target being fuzzed. We already said that on the target machine we should have the network and processor monitor running - those two will listen on port 26001 and 26002. So here we're declaring on which IP and port those two monitors are accessible. The sessions.target argument specifies the target to send the fuzzed data to, and the procmon_options specify the commands that can be used to start and stop the vulnerable server. We can test those commands on the target operation system in the cmd.exe as they should be runnable. We're using the wmic command to stop the vulnerable server process, while we're starting the vulnerable server just by executing the vulnserver.exe executable.

At the end of the vulnserver.py we're just grabbing a banner of the Vulnserver and we can start fuzzing.

""" start fuzzing - define target and data """
s.add_target(target)
s.fuzz()

The Results

Sulley is able to find all six vulnerabilities in the vulnerable commands KSTET, TRUN, GMON, GTER, HTER and LTER. It detects some of the vulnerabilities multiple times. Let's present the number of vulnerabilities the Sulley found when fuzzing the vulnerable server:

TRUN : 3x
GMON : 1x
KSTET: 4x
GTER : 2x
HTER : 1x
LTER : 1x

Sulley found 3 input cases where it was able to crash the vulnerable server while fuzzing the TRUN command, 1 input case that crashes vVulnserver while fuzzing the GMON command, etc. The input test case that crashes vulnerable server while fuzzing the command LTER is presented below:

LTER /.:/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n

Conclusion

We've seen how we can use Sulley to fuzz a vulnerable server. We also saw that Sulley was successful at finding all six vulnerabilities present in the vulnerable server. This concludes that fuzzing works and Sulley is a great tool to use when fuzzing certain software for new vulnerabilities.

Comments