Automation using Python and Scapy

The Scapy Python library makes life a lot easier for network forensic investigators, allowing them to write small scripts and making automation a lot easier. Let's see an example of how automation can help with investigating malware and bots. Let's open the example PCAP file in Wireshark:

We can see that the PCAP file contains only 67 packets and it looks as though most of the traffic is HTTP-based. Looking at the conversations, we can see we have four of them:

Let's have a look at the HTTP requests:

We can see that some POST data is being sent from 172.16.0.130 to 185.141.27.187. However, User-Agent doesn't seem to be obvious from the user's behavior. Open one of the conversations to view what sort of data we are looking at. After the TCP stream (not HTTP), we can see that the following data is being posted to the server:

  1. Read the packet-capture file in Python
  2. Parse the completed HTTP sessions and separate the HTTP header and the payload
  3. Check whether the HTTP traffic is from LokiBot using network IOCs
  4. Optional: extract and decode the payload

So, let's work on a script, as follows:

packets = rdpcap("loki-bot_network_traffic.pcap") 
for packet in packets: 
    if TCP in packet: 
        investigate_packets(packet) 

The preceding snippet of code does nothing but read the pcap file using the rdpcap function from scapy. The next line traverses over each packet in the pcap file, and if it finds a TCP packet, it sends it to the investigate_packet function. Let's see the investigate_packet function:

def investigate_packets(packet): 
        pack__name = '%s:%s --> %s' % (packet[IP].src,packet[IP].sport, packet[IP].dst) 
        if isCompletedSession(packet): 

The function receives the packet, and a pack__name variable is generated based on the source IP address, source port, and destination IP address. Next, the packet is passed to the isCompletedSession function to check whether the packet session was completed successfully:

def ifthesessioniscompleted(packet): 
        pack__name = '%s:%s --> %s' % (packet[IP].src,packet[IP].sport, packet[IP].dst) 
        p_queue[pack__name].append(packet) 
        for session in p_queue: 
                SYN_PKT     = False 
                PSH_ACK_PKT = False 
                ACK_FIN_PKT = False 
                PSH_ACK_FIN_PKT = False 
                for sp in p_queue[session]: 
                        if sp[TCP].flags == 2: 
                                SYN = True 
                        if sp[TCP].flags == 24: 
                                PSH_ACK = True 
                        if sp[TCP].flags == 17: 
                                ACK_FIN = True 
                        if sp[TCP].flags == 25: 
                                PSH_ACK_FIN = True 
                if (SYN and PSH_ACK and ACK_FIN) or PSH_ACK_FIN: 
                        return True 
        return False 

The preceding code will receive the packet, generate a packet name, and append the packet to a p_queue array based on the packet name. Next, for all the elements of p_queue, the elements are checked for TCP flags 2, 24, 17, and 25 denoting SYN, PUSH-ACK, ACK-FIN, and PUSH-ACK-FIN respectively. Finally, if SYN, PSH_ACK, and ACK_FIN are found set or PSH_ACK_FIN has been found set, it returns true, which denotes that the session completed successfully. Let's go back to our calling function:

http_header, http_data = extractHeaderAndPayload(packet_queue[pack__name]) 
                if isLokiBotTraffic(http_header): 

We start by extracting the header and payload for the HTTP packets and send the HTTP header to check whether the header is for LokiBot:

def isLokiBotTraffic(http_headers): 
        indicator_count = 0 
        content_key_pattern = re.compile("^([A-Z0-9]{8}$)") 
 
        if 'User-Agent' in http_headers and http_headers['User-Agent'] == 'Mozilla/4.08 (Charon; Inferno)': 
                return True 
 
        if 'HTTP-Method' in http_headers and http_headers['HTTP-Method'] == 'POST': 
                indicator_count += 1 
 
        if all(key in http_headers for key in ('User-Agent','Host','Accept','Content-Type','Content-Encoding', 'Content-Key')): 
                indicator_count +=1 
 
        if 'User-Agent' in http_headers and any(UAS_String in http_headers['User-Agent'] for UAS_String in ('Charon','Inferno')): 
                indicator_count +=1 
 
        if 'Content-Key' in http_headers and content_key_pattern.match(http_headers['Content-Key']): 
                indicator_count +=1 
 
        if indicator_count >= 3: 
                return True 
        else: 
                return False 

The preceding code will check for the LokiBot key IOCs. It checks whether the User-Agent contains 'Mozilla/4.08 (Charon; Inferno)', the HTTP method is POST, all the HTTP headers, such as Agent, Host, Accept, Content-Type, and Content-Encoding are present, and, most important, whether Content-Key is present. If three or more IOCs are matched, it returns true for the packet to be identified as LokiBot communication. Next, we have the following:

                       parsed_payload['Network'].update({'Source IP': packet[IP].src}) 
                        parsed_payload['Network'].update({'Source Port': packet[IP].sport}) 
                        parsed_payload['Network'].update({'Destination IP': packet[IP].dst}) 
                        parsed_payload['Network'].update({'Destination Port': packet[IP].dport}) 
                        parsed_payload['Network'].update({'HTTP URI': http_header['HTTP-URI']}) 
                        parsed_payload['Malware Artifacts/IOCs'].update({'HTTP Method': http_header['HTTP-Method']}) 
                        parsed_payload['Network'].update({'Destination Host': http_header['Host']}) 
                        parsed_payload['Network'].update({'Data Transmission Time': datetime.fromtimestamp(packet.time).isoformat()}) 
                        parsed_payload['Malware Artifacts/IOCs'].update({'User-Agent String': http_header['User-Agent']}) 
                        print parsed_payload 

The preceding code simply adds important details, such as Source IP, Source Port, Destination IP, Destination Port, HTTP URI, HTTP-Method, Destination Host, Transmission Time, and User-Agent to the dictionary object and prints it out, as shown here:

We can see that we have Malware/IOCs and network details presented here. We just saw how easily we can develop a script to identify malware on the wire.

The parts of the preceding script are taken from https://github.com/R3MRUM/loki-parse/blob/master/loki-parse.py; the original script hosted here also decodes the payload part of LokiBot and presents an in-depth analysis of the packets.

Let's download the original loki-parse.py Python 2.7 script written by R3MRUM by cloning the https://github.com/R3MRUM/loki-parse.git repository and run it as shown in the following screenshot:

We can see that by running the script, we get a lot of information. Let's scroll down for more:

Well, we see plenty of data being displayed, along with Hostname, Operating System, and much more:

We can see that we have Traffic Purpose listed as well, and this denotes the purpose such as Exfiltrate Application/ Credential Data. This is true since we saw that FileZilla credentials in the first few lines of the result. Looking further, we can see that we have keylogger data as well:

Also, looking at this packet detail, we can see that it has the Exfiltrate Keylogger Data type:

It is recommended you go through the script, as it contains many things that will aid you in developing identifier scripts for various malware and other IOCs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.126.74