This article shows how easy it is to create a fuzz for the MQTT protocol using the Polymorph framework.
It is assumed that the reader is familiar with the MQTT protocol. Otherwise, I recommend that you visit http://mqtt.org/. First, configure the working environment for fuzzing, which will run on the basis of Kali Linux with the following dependencies:
apt-get install build-essential python-dev libnetfilter-queue-dev tshark tcpdump python3-pip wireshark
pip3 install –process-dependency-links polymorph
apt-get install mosquitto mosquitto-clients
sudo apt-get install gcc make git wget
git clone https://github.com/aoh/radamsa.git&& cd radamsa && make && sudo make install
After all of the above packages are installed, before you design a fuzzer, you need to test the mosquitto message broker installed on the local host. We will use two terminals. The first client will subscribe to a specific topic, the second will post a message in the same topic. The figure below shows the results of the commands.
From this point on, you can begin to design the phasizer. Since both clients are in one place, and we do not need to intercept communication between the two machines, spoofing will not be used. Accordingly, to start the “listening” enough command capture.
At the moment, our task is to intercept one of the packages that we want to modify. After interception, the framework converts the package into a template, with which we will continue to work. During sniffing, you must send the MQTTPublish message to another client.
After the transfer, press Ctrl-C to complete the sniffing and enter the show command to view the intercepted packets on the screen.
As you can see in the figure above, most packages have a Raw layer. This fact means that, most likely, with the help of the main tools, it was not possible to intercept and correctly analyze the packages we need. Using the dissect command, we will use more advanced parsers to look at the hidden parts of the packages.
Now that we have a better understanding of the purpose of each byte in intercepted packets, we need to select a template that corresponds to the package that we want to modify. For more detailed information, you can use the wireshark command to open the application of the same name, and to access the selected template with the template command.
Now we are in the context of the selected template. Next, using the show command, you can see the various layers, fields, and types used in the template. The template concept is the most important abstraction of the Polymorph framework, which allows you to access intercepted packets in real time using the simplest code used to process packets. Moreover, when saving a session, all the conditional functions and structures of the framework are stored in a special repository.
Then execution proceeds to conditional functions, which are subdivided into three categories: precondition, postcondition, and execution. When a user enters the intercept command in the context of the current template, on the machine where the Polymorph framework is located, the package redirection is suspended at the kernel level and the transfer to the utility that is processing begins. For each intercepted packet, conditional functions that are user-defined will be executed.
Consider an example of how conditional functions work. Add a precondition function for the current template using the precs -a test_condition command.
If you use the pico editor, do not confuse spaces and tabs. It is better to use spaces to align the code. You can also specify an editor from the PATH variable with the -e option:
def test_precondition (packet):
print (“The next packet arrive:”)
Type “y” to save the code on the disk, and then type the precs -s command to view the function that was added as a precondition.
Next, enter the intercept command
Now we can observe in real time the transmission and processing of packets, as well as the function that we have added as a precondition. You can test the whole scheme by sending an MQTTPublish message from the MQTT client to the message broker.
Conditional functions are another important abstraction of the framework, which works as follows. When a packet is intercepted in real time, conditional functions specified by the user are executed in a certain order. First, precondition functions are executed from the preconditions section in the order in which the user added. Then the queue comes to the executable function from the executions section. At the very end, postconditions are executed from the postconditions section. If at any time any function returns None, the executing chain is terminated and the packet is sent in the current state. On the other hand, if the packet returned as an argument is returned, the execution of the chain of conditional functions continues. It should be remembered that the packet received as an argument is a package,
After we understand the logic of the conditional functions, exit the interception mode by pressing the Ctrl-C key combination, after which the system will simply redirect the packets and will not send the Polymorph framework for processing. Now let’s add some conditional functions in the preconditions, executions and postconditions sections that will be launched for each intercepted packet. To delete the current conditional function that we added, use the precs -d test_condition command.
Two preconditions will be added to the preconditions section:
precs -a global_vars -e editor
precs -a filter_mqttpublish -e editor
The first precondition function global_vars creates a global variable that will remain a constant for all intercepted packets. This variable will store the test scripts used to process the MQTTPublish packages.
def global_vars (packet):
setattr (packet, ‘fuzz_cases’, )
The second predefined filter_mqttpublish function will filter incoming packets in such a way that the entire subsequent chain of conditional functions will be executed only if the msgtype field is set to 48. Note that by using an abstraction based on the template, the Polymorph framework knows the position of the msgtype field among bytes of the intercepted packet and, accordingly, can easily access the proper byte.
def filter_mqttpublish (packet):
ifpacket [‘RAW.MQTT’] [‘msgtype’] == 48:
The function in the executions section is slightly more complicated than the previous two, but in general, everything is as simple as that. The code shown below performs the following tasks:
- Converts an intercepted packet to the Scapy application format to make it easier to work with the MQTT protocol fields. Especially when working with encoded field sizes. Some time ago I wrote an MQTT-specificationfor Scapy.
- Checks whether the processed values are in the list of test scenarios. The list is stored in the global variable created earlier. If the list is empty, the Radamsa function is started to generate new test scenarios, which will also be stored in the global variable.
- Uses Scapy to insert a test value into the msg field of the package and removes this value from the list (so that it does not insert twice). In addition, the control fields, such as lengths and chksums, are recalculated.
def insert_value (packet):
from os importlistdir
from os.path importjoin
from scapy.all importIP
from scapy.contrib.mqtt importMQTT
# Building a Scapy packet
pkt = IP (packet.raw [14:])
# fuzzing the case Retrieving
valid_cases = “valid_cases”
dpath = “fuzz_cases”
subprocess.check_call ([ “radamsa”,
join (dpath, “fuzz-% n% s.”),
” -n “,
” 58 “,
” -r “,
packet.fuzz_cases = [open (join (dpath, f), ‘rb’). read ()
forf inlistdir (dpath)]
# Inserting the value and recalculating some fields
del pkt [‘IP’] len
del pkt [ ‘IP’]. Chksum
del pkt [‘TCP’]. Chksum
del pkt [‘MQTT’].
Len del pkt [‘MQTTPublish’]. Length
pkt [‘MQTTPublish’]. Topic = packet.fuzz_cases.pop ()
pkt .show2 ()
packet.raw = bytes (pkt)
Now we have everything in order to create a fuzzer based on the Polymorph framework for the MQTT protocol. To run our construct, we need to create two directories that the PATH variable should reference and that will be used by the Polymorph framework. The first folder is valid_cases, the second is fuzz_cases. These directories will be used by the Radamsa function to read test scenarios and convert them into scenarios that might be suitable for exploiting the vulnerability. New scenarios are added
Once the scripts are added, switch to the template context and run the intercept command. Then, using the MQTT client, we send a message, which, if possible, should be long, so that there are no problems with sequence numbers in the sessions used in the TCP / IP protocol stack.
We can track package changes and the appearance of values sent by the Radamsa function in real time. Now we need to organize the simplest cycle in Bash, inside of which all test values will be checked. In addition, we can run testing inside the debugger to collect and then analyze the exceptions that arise.
#! / bin / bash
while true; do
mosquitto_pub -t `python3 -c” print (‘A’ * 10000) “-m ‘hello’
The save command, started from the template context, allows you to export a template. Subsequent import to the Polymorph framework on another machine is performed using the polymorph -t template.json command. Thus, you can share templates with your colleagues. My template can be downloaded using the link below: