Browse Source

Add first draft of new MembersMgmtCommAttack (for now only local communication is implemented)
Code organization will be done at a later point

dustin.born 7 years ago
parent
commit
b41bdf0c01
1 changed files with 138 additions and 20 deletions
  1. 138 20
      code/Attack/MembersMgmtCommAttack.py

+ 138 - 20
code/Attack/MembersMgmtCommAttack.py

@@ -1,12 +1,19 @@
-from random import randint
+from random import randint, randrange, choice
 from collections import deque
 from collections import deque
 
 
 from Attack import BaseAttack
 from Attack import BaseAttack
 from Attack.AttackParameters import Parameter as Param
 from Attack.AttackParameters import Parameter as Param
 from Attack.AttackParameters import ParameterTypes
 from Attack.AttackParameters import ParameterTypes
-from ID2TLib import IPPacketGenerator as IPPktGen, FileUtils
-from ID2TLib.IPGenerator import MappingIPGenerator 
 
 
+from ID2TLib import FileUtils
+from ID2TLib.PacketGenerator import PacketGenerator
+from ID2TLib.IPGenerator import MappingIPGenerator 
+from ID2TLib.PcapAddressOperations import PcapAddressOperations
+from ID2TLib.MapInputCSVToIDs import find_interval_with_most_comm
+from ID2TLib.MacAddressGenerator import MacAddressGenerator
+from ID2TLib.MessageType import MessageType
+from ID2TLib.genRandPort import generateRandomPort
+from ID2TLib import PaddingGenerator
 
 
 class MembersMgmtCommAttack(BaseAttack.BaseAttack):
 class MembersMgmtCommAttack(BaseAttack.BaseAttack):
     def __init__(self):
     def __init__(self):
@@ -28,11 +35,22 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
             Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
 
 
             # use num_attackers to specify number of communicating devices?
             # use num_attackers to specify number of communicating devices?
-            Param.NUMBER_ATTACKERS: ParameterTypes.TYPE_INTEGER_POSITIVE,
+            Param.NUMBER_BOTS: ParameterTypes.TYPE_INTEGER_POSITIVE,
 
 
             # input file containing botnet communication
             # input file containing botnet communication
             Param.FILE_CSV: ParameterTypes.TYPE_FILEPATH,
             Param.FILE_CSV: ParameterTypes.TYPE_FILEPATH,
-            Param.FILE_XML: ParameterTypes.TYPE_FILEPATH
+            Param.FILE_XML: ParameterTypes.TYPE_FILEPATH,
+
+            # the scope of communications
+            Param.COMM_TYPE: ParameterTypes.TYPE_COMM_TYPE,
+
+            # the percentage of IP reuse (if total and other is specified, percentages are multiplied)
+            Param.IP_REUSE_TOTAL: ParameterTypes.TYPE_PERCENTAGE,
+            Param.IP_REUSE_LOCAL: ParameterTypes.TYPE_PERCENTAGE,
+            Param.IP_REUSE_EXTERNAL: ParameterTypes.TYPE_PERCENTAGE,
+
+            # the user-selected padding to add to every packet
+            Param.PACKET_PADDING: ParameterTypes.TYPE_PADDING
         }
         }
 
 
     def init_params(self):
     def init_params(self):
@@ -44,16 +62,31 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         :param statistics: Reference to a statistics object.
         :param statistics: Reference to a statistics object.
         """
         """
         # set class constants
         # set class constants
-        self.MESSAGE_TYPES = {2: "SALITY", 3: "TIMEOUT"}
         self.DEFAULT_XML_PATH = "resources/MembersMgmtComm_example.xml"
         self.DEFAULT_XML_PATH = "resources/MembersMgmtComm_example.xml"
+        # threshold for ID to be recognized as rather common in communication
+        self.MOST_COMMON_THRES = 0.08
 
 
         # PARAMETERS: initialize with default values
         # PARAMETERS: initialize with default values
         # (values are overwritten if user specifies them)
         # (values are overwritten if user specifies them)
         self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
         self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
         self.add_param_value(Param.PACKETS_PER_SECOND, 0)
         self.add_param_value(Param.PACKETS_PER_SECOND, 0)
         self.add_param_value(Param.FILE_XML, self.DEFAULT_XML_PATH)
         self.add_param_value(Param.FILE_XML, self.DEFAULT_XML_PATH)
+        self.add_param_value(Param.ATTACK_DURATION, 100)
+        self.add_param_value(Param.NUMBER_BOTS, 20)
+
+        # default locality behavior
+        self.add_param_value(Param.COMM_TYPE, "mixed")
+        # TODO: change 1 to something better
+        self.add_param_value(Param.IP_REUSE_TOTAL, 1)
+        self.add_param_value(Param.IP_REUSE_LOCAL, 0.5)
+        self.add_param_value(Param.IP_REUSE_EXTERNAL, 0.5)
+
+        # add default additional padding
+        self.add_param_value(Param.PACKET_PADDING, 0)
+
         
         
     def generate_attack_pcap(self):
     def generate_attack_pcap(self):
+        # parse input CSV or XML
         filepath_xml = self.get_param_value(Param.FILE_XML)
         filepath_xml = self.get_param_value(Param.FILE_XML)
         filepath_csv = self.get_param_value(Param.FILE_CSV)
         filepath_csv = self.get_param_value(Param.FILE_CSV)
 
 
@@ -61,28 +94,104 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
         if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
             filepath_xml = FileUtils.parse_csv_to_xml(filepath_csv) 
             filepath_xml = FileUtils.parse_csv_to_xml(filepath_csv) 
 
 
-        comm_entries = FileUtils.parse_xml(filepath_xml)
+        abstract_packets = FileUtils.parse_xml(filepath_xml)
+        
+        # find a good communication mapping
+        duration = self.get_param_value(Param.ATTACK_DURATION)
+        number_bots = self.get_param_value(Param.NUMBER_BOTS)
+        comm_interval = find_interval_with_most_comm(abstract_packets, number_bots, duration)[0]
+
+        if comm_interval is None:
+            print("Error: There is no interval in the given CSV/XML that has enough communication")
+            return 0, None
+
+        mapped_ids, packet_start_idx, packet_end_idx = comm_interval["IDs"], comm_interval["Start"], comm_interval["End"]
+
+        # determine most common communicating IDs
+        total_mentions = 0
+        most_common_thres = self.MOST_COMMON_THRES
+        most_common_ids = {}
+        for id_ in mapped_ids:
+            total_mentions += mapped_ids[id_]
+
+        for id_ in mapped_ids:
+            count = mapped_ids[id_]
+            mentions_percentage = count / total_mentions
+            if mentions_percentage >= most_common_thres:
+                most_common_ids[id_] = mentions_percentage
+
+        # determine amount of reused IPs
+        reuse_percent_total = self.get_param_value(Param.IP_REUSE_TOTAL)
+        reuse_percent_external = self.get_param_value(Param.IP_REUSE_EXTERNAL)
+        reuse_percent_local = self.get_param_value(Param.IP_REUSE_LOCAL)
+
+        reuse_count_external = int(reuse_percent_total * reuse_percent_external * len(mapped_ids))
+        reuse_count_local = int(reuse_percent_total * reuse_percent_local * len(mapped_ids))
+
+        # create bot IP and MAC configs
+        macgen = MacAddressGenerator() 
+        comm_type = self.get_param_value(Param.COMM_TYPE)
+        pcapops = PcapAddressOperations(self.statistics)
+        bot_configs = {}
+
+        # where IP is external, already set MAC to router MAC here
+        if comm_type == "mixed":
+            ...
+        elif comm_type == "external":
+            ...
+        elif comm_type == "local":
+            existing_local_ips = pcapops.get_existing_priv_ips(reuse_count_local)
+            count_avail = min(reuse_count_local, len(existing_local_ips))
+            ids = set(mapped_ids.keys())
+            for ip in existing_local_ips:
+                random_id = choice(tuple(ids))
+                mac = self.statistics.process_db_query("macAddress(IPAddress=%s)" % ip)
+                bot_configs[random_id] = {"Type": "local", "IP": ip, "MAC":mac}
+                ids.remove(random_id)
+
+            count_remaining = number_bots - count_avail
+            new_local_ips = pcapops.get_new_priv_ips(count_remaining)
+            for ip in new_local_ips:
+                random_id = choice(tuple(ids))
+                mac = macgen.random_mac()
+                bot_configs[random_id] = {"Type": "local", "IP": ip, "MAC": mac}
+                ids.remove(random_id)
+
+
+        # create bot port configs
+        for bot in bot_configs:
+            bot_configs[bot]["Port"] = generateRandomPort()    
+
+        if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
+            filepath_xml = FileUtils.parse_csv_to_xml(filepath_csv) 
 
 
         # Setup initial parameters for packet creation
         # Setup initial parameters for packet creation
         BUFFER_SIZE = 1000
         BUFFER_SIZE = 1000
-        id_to_ip_mapper = MappingIPGenerator()
-        file_timestamp_prv = float(comm_entries[0]["Time"])
+        pkt_gen = PacketGenerator()
+        file_timestamp_prv = float(abstract_packets[packet_start_idx]["Time"])
         pcap_timestamp = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
         pcap_timestamp = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
-        duration = 0
+        padding = self.get_param_value(Param.PACKET_PADDING)
         packets = deque(maxlen=BUFFER_SIZE)
         packets = deque(maxlen=BUFFER_SIZE)
         total_pkts = 0
         total_pkts = 0
         limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
         limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
-        limit_duration = self.get_param_value(Param.ATTACK_DURATION)
+        limit_duration = duration
+        duration = 0
         path_attack_pcap = None
         path_attack_pcap = None
+        nl_requests = {}
 
 
         # create packets to write to pcap file
         # create packets to write to pcap file
-        for entry in comm_entries:
-            # map/retrieve ip addresses to ids from input file
-            ip_src = id_to_ip_mapper.get_mapped_ip(entry["Src"])
-            ip_dst = id_to_ip_mapper.get_mapped_ip(entry["Dst"])
-            
+        for abst_packet in abstract_packets[packet_start_idx:packet_end_idx]:
+            # map/retrieve addresses to ids from input file
+            id_src, id_dst = abst_packet["Src"], abst_packet["Dst"]
+            if (not id_src in bot_configs) or (not id_dst in bot_configs):
+                continue
+
+            ip_src, ip_dst = bot_configs[id_src]["IP"], bot_configs[id_dst]["IP"]
+            mac_src, mac_dst = bot_configs[id_src]["MAC"], bot_configs[id_dst]["MAC"]
+            port_src, port_dst = bot_configs[id_src]["Port"], bot_configs[id_dst]["Port"]
+
             # update timestamps and duration
             # update timestamps and duration
-            file_timestamp = float(entry["Time"])
+            file_timestamp = float(abst_packet["Time"])
             file_time_delta = file_timestamp - file_timestamp_prv
             file_time_delta = file_timestamp - file_timestamp_prv
             pcap_timestamp += file_time_delta
             pcap_timestamp += file_time_delta
             duration += file_time_delta
             duration += file_time_delta
@@ -94,8 +203,15 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 break
                 break
         
         
             # create ip packet and add to packets list
             # create ip packet and add to packets list
-            message_type = self.MESSAGE_TYPES[int(entry["Type"])]
-            packet = IPPktGen.generate_ip_packet(ip_src=ip_src, ip_dst=ip_dst, payload=message_type)
+            message_type = int(abst_packet["Type"])
+            nl_size = 0
+
+            if message_type == MessageType.SALITY_NL_REPLY.value:
+                nl_size = randint(1, 25)
+
+            packet = pkt_gen.generate_MMCOM_Packet(ip_src=ip_src, ip_dst=ip_dst, mac_src=mac_src, mac_dst=mac_dst, port_src=port_src, port_dst=port_dst, message_type=message_type, neighborlist_entries=nl_size)
+            PaddingGenerator.add_padding(packet, padding)
+
             packet.time = pcap_timestamp
             packet.time = pcap_timestamp
             packets.append(packet)
             packets.append(packet)
             total_pkts += 1
             total_pkts += 1
@@ -104,13 +220,15 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             if total_pkts <= 1 :
             if total_pkts <= 1 :
                 self.attack_start_utime = packets[0].time
                 self.attack_start_utime = packets[0].time
             elif total_pkts % BUFFER_SIZE == 0: # every 1000 packets write them to the pcap file (append)
             elif total_pkts % BUFFER_SIZE == 0: # every 1000 packets write them to the pcap file (append)
-                last_packet = packets[-1]
                 packets = list(packets)
                 packets = list(packets)
+                PaddingGenerator.equal_length(packets)
+                last_packet = packets[-1]
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 packets = deque(maxlen=BUFFER_SIZE)
                 packets = deque(maxlen=BUFFER_SIZE)
 
 
         if len(packets) > 0:
         if len(packets) > 0:
             packets = list(packets)
             packets = list(packets)
+            PaddingGenerator.equal_length(packets)
             path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
             path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
             last_packet = packets[-1]
             last_packet = packets[-1]