Pārlūkot izejas kodu

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 gadi atpakaļ
vecāks
revīzija
b41bdf0c01
1 mainītis faili ar 138 papildinājumiem un 20 dzēšanām
  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 Attack import BaseAttack
 from Attack.AttackParameters import Parameter as Param
 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):
     def __init__(self):
@@ -28,11 +35,22 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
 
             # 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
             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):
@@ -44,16 +62,31 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         :param statistics: Reference to a statistics object.
         """
         # set class constants
-        self.MESSAGE_TYPES = {2: "SALITY", 3: "TIMEOUT"}
         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
         # (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.PACKETS_PER_SECOND, 0)
         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):
+        # parse input CSV or XML
         filepath_xml = self.get_param_value(Param.FILE_XML)
         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:
             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
         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)
-        duration = 0
+        padding = self.get_param_value(Param.PACKET_PADDING)
         packets = deque(maxlen=BUFFER_SIZE)
         total_pkts = 0
         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
+        nl_requests = {}
 
         # 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
-            file_timestamp = float(entry["Time"])
+            file_timestamp = float(abst_packet["Time"])
             file_time_delta = file_timestamp - file_timestamp_prv
             pcap_timestamp += file_time_delta
             duration += file_time_delta
@@ -94,8 +203,15 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 break
         
             # 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
             packets.append(packet)
             total_pkts += 1
@@ -104,13 +220,15 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             if total_pkts <= 1 :
                 self.attack_start_utime = packets[0].time
             elif total_pkts % BUFFER_SIZE == 0: # every 1000 packets write them to the pcap file (append)
-                last_packet = packets[-1]
                 packets = list(packets)
+                PaddingGenerator.equal_length(packets)
+                last_packet = packets[-1]
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 packets = deque(maxlen=BUFFER_SIZE)
 
         if len(packets) > 0:
             packets = list(packets)
+            PaddingGenerator.equal_length(packets)
             path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
             last_packet = packets[-1]