The Mutiny Fuzzer Framework is a network fuzzer that operates by replaying PCAPs through a mutational fuzzer. The goal is to begin network fuzzing as quickly as possible, at the expense of being thorough.
The general workflow for Mutiny is to take a sample of legitimate traffic, such as a browser request, and feed it into a prep script to generate a .fuzzer file.
Then, Mutiny can be run with this .fuzzer file to generate traffic against a target host, mutating whichever packets the user would like.
There are extensions that allow changing how Mutiny behaves, including changing messages based on input/output, changing how Mutiny responds to network errors, and monitoring the target in a separate thread.
Mutiny uses Radamsa to perform mutations.
The Decept Proxy is a multi-purpose network proxy that can forward traffic from a plaintext or TLS TCP/UDP/domain socket connection to a plaintext or TLS TCP/UDP/domain socket connection, among other features.
It makes a good companion for Mutiny, as it can both generate .fuzzer files directly, particularly helpful when fuzzing TLS connections, and allow Mutiny to communicate with TLS hosts.
sample_apps give a basic idea of some things that can be done with the fuzzer, with a few different applications/clients to test with.
Also Read – Flightsim : Utility to Generate Malicious Network Traffic & Evaluate Controls
Setup
Ensure python and scapy are installed.
Untar Radamsa and make
(You do not have to make install, unless you want it
in /usr/bin – it will use the local Radamsa) Update mutiny.py
with path to
Radamsa if you changed it.
Basic Usage
Save pcap into a folder. Run mutiny_prep.py
on <XYZ>.pcap
(also optionally
pass the directory of a custom processor if any, more below). Answer the
questions, end up with a <XYZ>.fuzzer
file in same folder as pcap.
Run mutiny.py <XYZ>.fuzzer <targetIP>
This will start fuzzing. Logs will be
saved in same folder, under directory
<XYZ>_logs/<time_of_session>/<seed_number>
More Detailed Usage
.fuzzer Files
The .fuzzer files are human-readable and commented. They allow changing various options on a per-fuzzer-file basis, including which message or message parts are fuzzed.
Message Formatting
Within a .fuzzer file is the message contents. These are simply lines that begin with either ‘inbound’ or ‘outbound’, signifying which direction the message goes. They are in Python string format, with ‘\xYY’ being used for non-printable characters. These are autogenerated by ‘mutiny_prep.py’ and Decept, but sometimes need to be manually modified.
Message Formatting – Manual Editing
If a message has the ‘fuzz’ keyword after ‘outbound’, this indicates it is to be fuzzed through Radamsa. A given message can have line continuations, by simply putting more message data in quotes on a new line. In this case, this second line will be merged with the first.
Alternatively, the ‘sub’ keyword can be used to indicate a subcomponent. This allows specifying a separate component of the message, in order to fuzz only certain parts and for convenience within a Message Processor.
Here is an example arbitrary set of message data:
outbound ‘say’
‘ hi’
sub fuzz ‘ and fuzz’
‘ this’
sub ‘ but not this\xde\xad\xbe\xef’
inbound ‘this is the server’s’
‘ expected response’
This will cause Mutiny to transmit say hi and fuzz this but not this(0xdeadbeef)
. 0xdeadbeef
will be transmitted as 4 hex bytes. and fuzz this
will be passed through Radamsa for fuzzing, but say hi
and but not this(0xdeadbeef)
will be left alone.
Mutiny will wait for a response from the server after transmitting the single above message, due to the ‘inbound’ line. The server’s expected response is this is the server's expected response
.
Mutiny won’t do a whole lot with this data, aside from seeing if what the server actually sent matches this string. If a crash occurs, Mutiny will log both the expected output from the server and what the server actually replied with.
Customization
mutiny_classes/ contains base classes for the Message Processor, Monitor, and Exception Processor. Any of these files can be copied into the same folder as the .fuzzer (by default) or into a separate subfolder specified as the ‘processor_dir’ within the .fuzzer file.
These three classes allow for storing server responses and changing outgoing messages, monitoring the target on a separate thread, and changing how Mutiny handles exceptions.
Customization – Message Processor
The Message Processor defines various callbacks that are called during a fuzzing run. Within these callbacks, any Python code can be run. Anecdotally, these are primarily used in three ways.
The most common is when the server sends tokens that need to be added to future
outbound messages. For example, if Mutiny’s first message logs in, and the
server responds with a session ID, the postReceiveProcess()
callback can be used
to store that session ID. Then, in preSendProcess()
, the outgoing data can be
fixed up with that session ID. An example of this is in
sample_apps/session_server
.
Another common use of a Message Processor is to limit or change a fuzzed message. For example, if the server always drops messages greater than 1000 bytes, it may not be worth sending any large messages. preSendProcess() can be used to shorten messages after fuzzing but before they are sent or to raise an exception.
Raising an exception brings up the final way Message Processors are commonly
used. Within a callback, any custom exceptions defined in
mutiny_classes/mutiny_exceptions.py
can be raised. There are several
exceptions, all commented, that will cause various behaviors from Mutiny. These
generally involve either logging, retrying, or aborting the current run.
Customization – Monitor
The Monitor has a monitorTarget()
function that is run on a separate thread from
the main Mutiny fuzzer. The purpose is to allow implementing a long-running
process that can monitor a host in some fashion. This can be anything that can
be done in Python, such as communicating with a monitor daemon running on the
target, reading a long file, or even just pinging the host repeatedly, depending
on the requirements of the fuzzing session.
If the Monitor detects a crash, it can call signalMain()
at any time. This will
signal the main Mutiny thread that a crash has occurred, and it will log the
crash. This function should generally operate in an infinite loop, as returning
will cause the thread to terminate, and it will not be restarted.
Customization – Exception Processor
The Exception Processor determines what Mutiny should do with a given exception
during a fuzz session. In the most general sense, the processException()
function will translate Python and OS-level exceptions into Mutiny error
handling actions as best as it can.
For example, if Mutiny gets ‘Connection Refused’, the default response is to
assume that the target server has died unrecoverably, so Mutiny will log the
previous run and halt. This is true in most cases, but this behavior can be
changed to that of any of the exceptions in
mutiny_classes/mutiny_exceptions.py
as needed, allowing tailoring of crash
detection and error correction.
Credit: James Spadaro & Lilith Wyatt