MembersMgmtCommAttack.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. from random import randint, randrange, choice
  2. from collections import deque
  3. from Attack import BaseAttack
  4. from Attack.AttackParameters import Parameter as Param
  5. from Attack.AttackParameters import ParameterTypes
  6. from ID2TLib import FileUtils
  7. from ID2TLib.PacketGenerator import PacketGenerator
  8. from ID2TLib.IPGenerator import MappingIPGenerator
  9. from ID2TLib.PcapAddressOperations import PcapAddressOperations
  10. from ID2TLib.MapInputCSVToIDs import find_interval_with_most_comm
  11. from ID2TLib.MacAddressGenerator import MacAddressGenerator
  12. from ID2TLib.MessageType import MessageType
  13. from ID2TLib.genRandPort import generateRandomPort
  14. from ID2TLib import PaddingGenerator
  15. class MembersMgmtCommAttack(BaseAttack.BaseAttack):
  16. def __init__(self):
  17. """
  18. Creates a new instance of the Membership Management Communication.
  19. """
  20. # Initialize communication
  21. super(MembersMgmtCommAttack, self).__init__("Membership Management Communication Attack (MembersMgmtCommAttack)",
  22. "Injects Membership Management Communication", "Botnet communication")
  23. # Define allowed parameters and their type
  24. self.supported_params = {
  25. # parameters regarding attack
  26. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  27. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  28. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  29. Param.PACKETS_LIMIT: ParameterTypes.TYPE_INTEGER_POSITIVE,
  30. Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
  31. # use num_attackers to specify number of communicating devices?
  32. Param.NUMBER_BOTS: ParameterTypes.TYPE_INTEGER_POSITIVE,
  33. # input file containing botnet communication
  34. Param.FILE_CSV: ParameterTypes.TYPE_FILEPATH,
  35. Param.FILE_XML: ParameterTypes.TYPE_FILEPATH,
  36. # the scope of communications
  37. Param.COMM_TYPE: ParameterTypes.TYPE_COMM_TYPE,
  38. # the percentage of IP reuse (if total and other is specified, percentages are multiplied)
  39. Param.IP_REUSE_TOTAL: ParameterTypes.TYPE_PERCENTAGE,
  40. Param.IP_REUSE_LOCAL: ParameterTypes.TYPE_PERCENTAGE,
  41. Param.IP_REUSE_EXTERNAL: ParameterTypes.TYPE_PERCENTAGE,
  42. # the user-selected padding to add to every packet
  43. Param.PACKET_PADDING: ParameterTypes.TYPE_PADDING
  44. }
  45. def init_params(self):
  46. """
  47. Initialize some parameters of this communication-attack using the user supplied command line parameters.
  48. The remaining parameters are implicitly set in the provided data file. Note: the timestamps in the file
  49. have to be sorted in ascending order
  50. :param statistics: Reference to a statistics object.
  51. """
  52. # set class constants
  53. self.DEFAULT_XML_PATH = "resources/MembersMgmtComm_example.xml"
  54. # threshold for ID to be recognized as rather common in communication
  55. self.MOST_COMMON_THRES = 0.08
  56. # PARAMETERS: initialize with default values
  57. # (values are overwritten if user specifies them)
  58. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  59. self.add_param_value(Param.PACKETS_PER_SECOND, 0)
  60. self.add_param_value(Param.FILE_XML, self.DEFAULT_XML_PATH)
  61. self.add_param_value(Param.ATTACK_DURATION, 100)
  62. self.add_param_value(Param.NUMBER_BOTS, 20)
  63. # default locality behavior
  64. self.add_param_value(Param.COMM_TYPE, "mixed")
  65. # TODO: change 1 to something better
  66. self.add_param_value(Param.IP_REUSE_TOTAL, 1)
  67. self.add_param_value(Param.IP_REUSE_LOCAL, 0.5)
  68. self.add_param_value(Param.IP_REUSE_EXTERNAL, 0.5)
  69. # add default additional padding
  70. self.add_param_value(Param.PACKET_PADDING, 0)
  71. def generate_attack_pcap(self):
  72. # parse input CSV or XML
  73. filepath_xml = self.get_param_value(Param.FILE_XML)
  74. filepath_csv = self.get_param_value(Param.FILE_CSV)
  75. # prefer XML input over CSV input
  76. if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
  77. filepath_xml = FileUtils.parse_csv_to_xml(filepath_csv)
  78. abstract_packets = FileUtils.parse_xml(filepath_xml)
  79. # find a good communication mapping
  80. duration = self.get_param_value(Param.ATTACK_DURATION)
  81. number_bots = self.get_param_value(Param.NUMBER_BOTS)
  82. comm_interval = find_interval_with_most_comm(abstract_packets, number_bots, duration)[0]
  83. if comm_interval is None:
  84. print("Error: There is no interval in the given CSV/XML that has enough communication")
  85. return 0, None
  86. mapped_ids, packet_start_idx, packet_end_idx = comm_interval["IDs"], comm_interval["Start"], comm_interval["End"]
  87. # determine most common communicating IDs
  88. total_mentions = 0
  89. most_common_thres = self.MOST_COMMON_THRES
  90. most_common_ids = {}
  91. for id_ in mapped_ids:
  92. total_mentions += mapped_ids[id_]
  93. for id_ in mapped_ids:
  94. count = mapped_ids[id_]
  95. mentions_percentage = count / total_mentions
  96. if mentions_percentage >= most_common_thres:
  97. most_common_ids[id_] = mentions_percentage
  98. # determine amount of reused IPs
  99. reuse_percent_total = self.get_param_value(Param.IP_REUSE_TOTAL)
  100. reuse_percent_external = self.get_param_value(Param.IP_REUSE_EXTERNAL)
  101. reuse_percent_local = self.get_param_value(Param.IP_REUSE_LOCAL)
  102. reuse_count_external = int(reuse_percent_total * reuse_percent_external * len(mapped_ids))
  103. reuse_count_local = int(reuse_percent_total * reuse_percent_local * len(mapped_ids))
  104. # create bot IP and MAC configs
  105. macgen = MacAddressGenerator()
  106. comm_type = self.get_param_value(Param.COMM_TYPE)
  107. pcapops = PcapAddressOperations(self.statistics)
  108. bot_configs = {}
  109. # where IP is external, already set MAC to router MAC here
  110. if comm_type == "mixed":
  111. ...
  112. elif comm_type == "external":
  113. ...
  114. elif comm_type == "local":
  115. existing_local_ips = pcapops.get_existing_priv_ips(reuse_count_local)
  116. count_avail = min(reuse_count_local, len(existing_local_ips))
  117. ids = set(mapped_ids.keys())
  118. for ip in existing_local_ips:
  119. random_id = choice(tuple(ids))
  120. mac = self.statistics.process_db_query("macAddress(IPAddress=%s)" % ip)
  121. bot_configs[random_id] = {"Type": "local", "IP": ip, "MAC":mac}
  122. ids.remove(random_id)
  123. count_remaining = number_bots - count_avail
  124. new_local_ips = pcapops.get_new_priv_ips(count_remaining)
  125. for ip in new_local_ips:
  126. random_id = choice(tuple(ids))
  127. mac = macgen.random_mac()
  128. bot_configs[random_id] = {"Type": "local", "IP": ip, "MAC": mac}
  129. ids.remove(random_id)
  130. # create bot port configs
  131. for bot in bot_configs:
  132. bot_configs[bot]["Port"] = generateRandomPort()
  133. if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
  134. filepath_xml = FileUtils.parse_csv_to_xml(filepath_csv)
  135. # Setup initial parameters for packet creation
  136. BUFFER_SIZE = 1000
  137. pkt_gen = PacketGenerator()
  138. file_timestamp_prv = float(abstract_packets[packet_start_idx]["Time"])
  139. pcap_timestamp = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  140. padding = self.get_param_value(Param.PACKET_PADDING)
  141. packets = deque(maxlen=BUFFER_SIZE)
  142. total_pkts = 0
  143. limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
  144. limit_duration = duration
  145. duration = 0
  146. path_attack_pcap = None
  147. nl_requests = {}
  148. # create packets to write to pcap file
  149. for abst_packet in abstract_packets[packet_start_idx:packet_end_idx]:
  150. # map/retrieve addresses to ids from input file
  151. id_src, id_dst = abst_packet["Src"], abst_packet["Dst"]
  152. if (not id_src in bot_configs) or (not id_dst in bot_configs):
  153. continue
  154. ip_src, ip_dst = bot_configs[id_src]["IP"], bot_configs[id_dst]["IP"]
  155. mac_src, mac_dst = bot_configs[id_src]["MAC"], bot_configs[id_dst]["MAC"]
  156. port_src, port_dst = bot_configs[id_src]["Port"], bot_configs[id_dst]["Port"]
  157. # update timestamps and duration
  158. file_timestamp = float(abst_packet["Time"])
  159. file_time_delta = file_timestamp - file_timestamp_prv
  160. pcap_timestamp += file_time_delta
  161. duration += file_time_delta
  162. file_timestamp_prv = file_timestamp
  163. # if total number of packets has been sent or the attack duration has been exceeded, stop
  164. if ((limit_packetcount is not None and total_pkts >= limit_packetcount) or
  165. (limit_duration is not None and duration >= limit_duration)):
  166. break
  167. # create ip packet and add to packets list
  168. message_type = int(abst_packet["Type"])
  169. nl_size = 0
  170. if message_type == MessageType.SALITY_NL_REPLY.value:
  171. nl_size = randint(1, 25)
  172. 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)
  173. PaddingGenerator.add_padding(packet, padding)
  174. packet.time = pcap_timestamp
  175. packets.append(packet)
  176. total_pkts += 1
  177. # Store timestamp of first packet (for attack label)
  178. if total_pkts <= 1 :
  179. self.attack_start_utime = packets[0].time
  180. elif total_pkts % BUFFER_SIZE == 0: # every 1000 packets write them to the pcap file (append)
  181. packets = list(packets)
  182. PaddingGenerator.equal_length(packets)
  183. last_packet = packets[-1]
  184. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  185. packets = deque(maxlen=BUFFER_SIZE)
  186. if len(packets) > 0:
  187. packets = list(packets)
  188. PaddingGenerator.equal_length(packets)
  189. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  190. last_packet = packets[-1]
  191. # Store timestamp of last packet
  192. self.attack_end_utime = last_packet.time
  193. # Return packets sorted by packet by timestamp and total number of packets (sent)
  194. return total_pkts , path_attack_pcap