A Simple FTP Fuzzer With Boofuzz

Joshua Pereyda
4 min readFeb 18, 2016

When I was looking for a protocol fuzzer, and looking into Sulley in particular, the only tangible example I found was a simple FTP fuzzer at securepla.net. It was a helpful getting started tutorial, and I hope this post does the same for boofuzz.

I’ve uploaded the example at https://github.com/jtpereyda/boofuzz-ftp. In this article, I’ll walk through the writing process.

Unit Under Test

You’ll need an FTP server to test. Obscure open source projects make for good practice. I used an FTP project by Siim: https://github.com/Siim/ftp.

Warning: This test uses the FTP STOR command, which writes files to disk. Testing this process while running on your own machine may result in unintended files being written who-knows-where. The safest way is to run the process under test on a separate VM. You have been warned. :)

Boilerplate

Edit: The boilerplate has been severely shortened since the writing of this article. Here is an updated example (see https://github.com/jtpereyda/boofuzz-ftp current code):

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

[End edit]

Like Sulley, boofuzz is a Python library that you use to write your own fuzzer. So our FTP fuzzer will go into its own script, which we’ll call ftp.py.

logger = FuzzLogger(fuzz_loggers=[FuzzLoggerText()])
session = sessions.Session(sleep_time=0.0, fuzz_data_logger=logger)
my_connection = SocketConnection("127.0.0.1", 8021, proto='tcp')
target = sessions.Target(my_connection)
session.add_target(target)

Everything revolves around the session object. First, we create a FuzzLogger object to pass into the Session constructor. In this case, we use a simple text logger.

When creating Session, we also set sleep_time, the number of seconds to sleep between tests, to zero.

We also create a connection object. This example uses a SocketConnection. The only other connection type available right now is SerialConenction.

The connection gets passed to a new Target object, and the Target gets added to the Session.

Phew! That’s it for boring boilerplate. This is something that boofuzz can still improve on. Now for the fun part.

Protocol Definition

Message Definition

Protocols generally consist of several messages, though you could have a protocol with only one message. To define a message, we use a set of macros inherited from Sulley:

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

s_initialize is a special method that starts a new message. This message is named “user”. The other macros are used for different kinds of data. s_delim is used for delimiters. s_string is used for string data… OK, you get the idea.

s_static is not fuzzable, so it might be worth changing it here so that you can see what happens when that part changes.

“USER” in all caps is part of the file transfer protocol (FTP), and we specify the user name as “anonymous.” Each of these will be mutated as the fuzzer runs.

Here are four of our messages, testing the USER, PASS, STOR, and RETR commands:

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")

Message Linking

Once you’ve defined your messages, you need to tell boofuzz the order in which to use them. We’ll start each session with USER and PASS, but diverge and send either the STOR or the RETR command:

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"))

Execute

The last step is to call your Session object’s fuzz method. If you’ve been putting this all in a main() method (which you’ve been doing, right?), your file will look like:

#!/usr/bin/env python
# Designed for use with boofuzz v0.0.1-dev3
from boofuzz import *
def main():
session = Session(
target=Target(
connection=SocketConnection("127.0.0.1", 8021, 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()

And of course you can execute it using Python:

python ftp.py  # > fuzz-logs.txt

You can see it in action by looking at the log output, or by watching your server application if it has console output.

Since we haven’t added any monitoring or test instrumentation, boofuzz will only detect a failure if the unit under test crashes, and it will not be able to restart the process.

And there you have it! A very simple fuzzer built with boofuzz. The next steps for a real fuzzer might include:

  • Add a process monitor. A process monitor runs the process under test, detects crashes, restarts the process, etc. This may not always be an option.
  • Add a post_send method to analyze the replies for anomalies. This is a good idea for any protocol test, but can involve a lot of work.
  • Expand the protocol definition to test more aspects of FTP.

That’s it, and happy fuzzing!

Have any other tips, or requests for future tutorials? Leave a comment below!

--

--