The slides ca be found over here
When doing stateful fuzzing, our protocol message requests aren't independent any more from one another and must be sent in a specific order so as to carry out the fuzzing process. This type of ordering leads to a natural representation of the state machine as a graph with message requests being represented as nodes and sequencing between messages as edges. The fuzzer, in this case, will walk all the paths in the graph, incrementally sending the correct messages to reach a certain node. The fuzzing process is complete when all paths have been explored.
Let's recap a bit what we've learned about the Sulley Fuzzing Framework. A general overview of its architecture is given in the figure above. Some of the components and their use:
In our previous lab we've been mainly focused on the data generation step and stateless fuzzing and left Sulley session management and the other components for this one.
The major benefit of Sulley is the fact that is allows “deep” fuzzing within a protocol. This is accomplished by linking requests together in a graph (or session in Sulley parlance) as shown in the following figure depicting such a graph for SMTP.
The code used for creating the graph and requests is presented below :
from sulley import * s_initialize("helo") s_static("helo") s_initialize("ehlo") s_static("ehlo") s_initialize("mail from") s_static("mail from") s_initialize("rcpt to") s_static("rcpt to") s_initialize("data") s_static("data") sess = sessions.session() sess.connect(s_get("helo")) sess.connect(s_get("ehlo")) sess.connect(s_get("helo"), s_get("mail from")) sess.connect(s_get("ehlo"), s_get("mail from")) sess.connect(s_get("mail from"), s_get("rcpt to")) sess.connect(s_get("rcpt to"), s_get("data")) fh = open("session_test.udg", "w+") fh.write(sess.render_graph_udraw()) fh.close()
The graph can be dumped to an UDG file for rendering in uDraw as shown near the end of the code snippet.
When fuzzing, Sulley walks the graph structure starting with the root node and fuzzing each component along the way. In this example it will begin with the 'helo' request. Once complete, Sulley will begin fuzzing the 'mail from' request. It does so by prefixing each test case with a valid 'helo' request. Next, Sulley moves on to fuzzing the 'rcpt to' request. Again, this is accomplished by prefixing each test case with a valid 'helo' and 'mail from' request.
The process continues through 'data' and then restarts down the 'ehlo' path.
When instantiating a session, the following optional keywords arguments may be specified:
Another advanced feature that Sulley introduces is the ability to register callbacks on every edge defined within the protocol graph structure. This allows us to register a function to call between node transmissions to implement functionality such as challenge response systems.
The callback method must follow this prototype and is passed as the thrid argument to session.connect:
def callback(node, edge, last_recv, sock)
Where:
A callback is also useful in situations where, for example, the size of the next pack is specified in the first packet. As another example, if you need to fill in the dynamic IP address of the target register a callback that snags the IP from sock.getpeername().
Edge callbacks can also be registered through the optional keyword argument 'callback' to the session.connect() method.
An example of the places where callbacks may be registered is given in the following figure.
In previous session on stateless fuzzing, we had problems with Sulley not restarting the process properly after it crashed.
In order to fix that, the start_target function defined in process_monitor_unix.py must return True since the check is done against the value in sulley/session.py and if it's false (which is the default) it considers the restart procedure failed. Also, the stop_target function must return True as well. By default it returns None which corresponds to False when used in an if statement.
Sulley is designed (but not fully implemented yet) to support parallel fuzzing by adding more targets. When more than one target is present, the fuzz tests are divided between all targets and are run in parallel. For each target, you will need a separate process monitor and network monitor to be instantiated. These can either be on the same machine (different ports) or on different machines.
During the previous Stateless Fuzzing session we've fuzzed only text based protocols but Sulley can also be used for binary ones so this time we can have a go at DNS. You may start your fuzzing from the examples/mdns.py fuzzer found in Sulley.
Your practice target will be the following DNS server written by someone who wanted to practice his networking skills. Let's see how many crashes you can find in it.
git clone https://github.com/francois/dnsd
Now, for the real thing. Your target will be the following open source DNS server:
git clone https://github.com/samboy/MaraDNS
MaraDNS is a tough nut to crack so, don't be disappointed if you won't find any crashes. The main challenge for this exercise is to orchestrate and automate the fuzzing for this real application.
Please sync the following Github repository which contains a simple FTP Server written in C, compile it, look over how it's configured and attempt to develop a stateful Sulley FTP fuzzer for it. A reference for the FTP state machine can be found in RFC959 int the 6. STATE DIAGRAMS section.
git clone https://github.com/xu-wang11/FtpServer.git
If you want a head start on the FTP fuzzer, you may find some code and logic for it over here
Now, after you had enough playing with the toy target, let's try and go for something real:
git clone https://github.com/jedisct1/pure-ftpd
This is a free-form exercise. You can select any application you want to fuzz and go build a fuzzer for it. Eg. applications: