MS17ScanAttack.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import logging
  2. import random as rnd
  3. import lea
  4. import scapy.layers.inet as inet
  5. import scapy.utils
  6. import Attack.AttackParameters as atkParam
  7. import Attack.BaseAttack as BaseAttack
  8. import ID2TLib.SMBLib as SMBLib
  9. import ID2TLib.Utility as Util
  10. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  11. # noinspection PyPep8
  12. class MS17ScanAttack(BaseAttack.BaseAttack):
  13. template_scan_pcap_path = Util.RESOURCE_DIR + "Win7_eternalblue_scan.pcap"
  14. # Empirical values from Metasploit experiments
  15. minDefaultPort = 30000
  16. maxDefaultPort = 50000
  17. last_conn_dst_port = 4444
  18. def __init__(self):
  19. """
  20. Creates a new instance of the EternalBlue Exploit.
  21. """
  22. # Initialize attack
  23. super(MS17ScanAttack, self).__init__("MS17ScanAttack", "Injects a MS17 scan'",
  24. "Scanning/Probing")
  25. self.pkt_num = 0
  26. self.path_attack_pcap = None
  27. # Define allowed parameters and their type
  28. self.supported_params.update({
  29. atkParam.Parameter.MAC_SOURCE: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  30. atkParam.Parameter.IP_SOURCE: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  31. atkParam.Parameter.PORT_SOURCE: atkParam.ParameterTypes.TYPE_PORT,
  32. atkParam.Parameter.MAC_DESTINATION: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  33. atkParam.Parameter.IP_DESTINATION: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  34. atkParam.Parameter.PORT_DESTINATION: atkParam.ParameterTypes.TYPE_PORT,
  35. atkParam.Parameter.INJECT_AT_TIMESTAMP: atkParam.ParameterTypes.TYPE_FLOAT,
  36. atkParam.Parameter.INJECT_AFTER_PACKET: atkParam.ParameterTypes.TYPE_PACKET_POSITION,
  37. atkParam.Parameter.PACKET_LIMIT_PER_SECOND: atkParam.ParameterTypes.TYPE_FLOAT
  38. })
  39. def init_params(self):
  40. """
  41. Initialize the parameters of this attack using the user supplied command line parameters.
  42. Use the provided statistics to calculate default parameters and to process user
  43. supplied queries.
  44. """
  45. # PARAMETERS: initialize with default utilsvalues
  46. # (values are overwritten if user specifies them)
  47. # Attacker configuration
  48. most_used_ip_address = self.statistics.get_most_used_ip_address()
  49. random_ip_address = self.statistics.get_random_ip_address()
  50. while random_ip_address == most_used_ip_address:
  51. random_ip_address = self.statistics.get_random_ip_address()
  52. self.add_param_value(atkParam.Parameter.IP_SOURCE, random_ip_address)
  53. self.add_param_value(atkParam.Parameter.MAC_SOURCE, self.statistics.get_mac_address(random_ip_address))
  54. self.add_param_value(atkParam.Parameter.PORT_SOURCE, rnd.randint(self.minDefaultPort, self.maxDefaultPort))
  55. # Victim configuration
  56. self.add_param_value(atkParam.Parameter.IP_DESTINATION, most_used_ip_address)
  57. destination_mac = self.statistics.get_mac_address(most_used_ip_address)
  58. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  59. destination_mac = self.generate_random_mac_address()
  60. self.add_param_value(atkParam.Parameter.MAC_DESTINATION, destination_mac)
  61. self.add_param_value(atkParam.Parameter.PORT_DESTINATION, SMBLib.smb_port)
  62. # Attack configuration
  63. self.add_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND,
  64. (self.statistics.get_pps_sent(most_used_ip_address) +
  65. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  66. self.add_param_value(atkParam.Parameter.INJECT_AFTER_PACKET, rnd.randint(0, self.statistics.get_packet_count()))
  67. def generate_attack_packets(self):
  68. """
  69. Creates the attack packets.
  70. """
  71. # Timestamp
  72. timestamp_next_pkt = self.get_param_value(atkParam.Parameter.INJECT_AT_TIMESTAMP)
  73. pps = self.get_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND)
  74. # calculate complement packet rates of BG traffic per interval
  75. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  76. # Initialize parameters
  77. self.packets = []
  78. mac_source = self.get_param_value(atkParam.Parameter.MAC_SOURCE)
  79. ip_source = self.get_param_value(atkParam.Parameter.IP_SOURCE)
  80. port_source = self.get_param_value(atkParam.Parameter.PORT_SOURCE)
  81. mac_destination = self.get_param_value(atkParam.Parameter.MAC_DESTINATION)
  82. ip_destination = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  83. port_destination = self.get_param_value(atkParam.Parameter.PORT_DESTINATION)
  84. # Check ip.src == ip.dst
  85. self.ip_src_dst_equal_check(ip_source, ip_destination)
  86. # Set TTL based on TTL distribution of IP address
  87. source_ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  88. if len(source_ttl_dist) > 0:
  89. source_ttl_prob_dict = lea.Lea.fromValFreqsDict(source_ttl_dist)
  90. source_ttl_value = source_ttl_prob_dict.random()
  91. else:
  92. source_ttl_value = Util.handle_most_used_outputs(self.statistics.get_most_used_ttl_value())
  93. destination_ttl_dist = self.statistics.get_ttl_distribution(ip_destination)
  94. if len(destination_ttl_dist) > 0:
  95. destination_ttl_prob_dict = lea.Lea.fromValFreqsDict(destination_ttl_dist)
  96. destination_ttl_value = destination_ttl_prob_dict.random()
  97. else:
  98. destination_ttl_value = Util.handle_most_used_outputs(
  99. self.statistics.get_most_used_ttl_value())
  100. # Set Window Size based on Window Size distribution of IP address
  101. source_win_dist = self.statistics.get_win_distribution(ip_source)
  102. if len(source_win_dist) > 0:
  103. source_win_prob_dict = lea.Lea.fromValFreqsDict(source_win_dist)
  104. else:
  105. source_win_dist = self.statistics.get_win_distribution(self.statistics.get_most_used_ip_address())
  106. source_win_prob_dict = lea.Lea.fromValFreqsDict(source_win_dist)
  107. destination_win_dist = self.statistics.get_win_distribution(ip_destination)
  108. if len(destination_win_dist) > 0:
  109. destination_win_prob_dict = lea.Lea.fromValFreqsDict(destination_win_dist)
  110. else:
  111. destination_win_dist = self.statistics.get_win_distribution(self.statistics.get_most_used_ip_address())
  112. destination_win_prob_dict = lea.Lea.fromValFreqsDict(destination_win_dist)
  113. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  114. mss_value = Util.handle_most_used_outputs(self.statistics.get_most_used_mss_value())
  115. if not mss_value:
  116. mss_value = 1465
  117. # Scan (MS17)
  118. # Read Win7_eternalblue_scan pcap file
  119. orig_ip_dst = None
  120. exploit_raw_packets = scapy.utils.RawPcapReader(self.template_scan_pcap_path)
  121. inter_arrival_times = self.get_inter_arrival_time(exploit_raw_packets)
  122. exploit_raw_packets.close()
  123. exploit_raw_packets = scapy.utils.RawPcapReader(self.template_scan_pcap_path)
  124. source_origin_wins, destination_origin_wins = {}, {}
  125. for self.pkt_num, pkt in enumerate(exploit_raw_packets):
  126. eth_frame = inet.Ether(pkt[0])
  127. ip_pkt = eth_frame.payload
  128. tcp_pkt = ip_pkt.payload
  129. if self.pkt_num == 0:
  130. if tcp_pkt.getfieldval("dport") == SMBLib.smb_port:
  131. orig_ip_dst = ip_pkt.getfieldval("dst") # victim IP
  132. # Request
  133. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  134. # Ether
  135. eth_frame.setfieldval("src", mac_source)
  136. eth_frame.setfieldval("dst", mac_destination)
  137. # IP
  138. ip_pkt.setfieldval("src", ip_source)
  139. ip_pkt.setfieldval("dst", ip_destination)
  140. ip_pkt.setfieldval("ttl", source_ttl_value)
  141. # TCP
  142. tcp_pkt.setfieldval("sport", port_source)
  143. tcp_pkt.setfieldval("dport", port_destination)
  144. # Window Size (mapping)
  145. source_origin_win = tcp_pkt.getfieldval("window")
  146. if source_origin_win not in source_origin_wins:
  147. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  148. new_win = source_origin_wins[source_origin_win]
  149. tcp_pkt.setfieldval("window", new_win)
  150. # MSS
  151. tcp_options = tcp_pkt.getfieldval("options")
  152. if tcp_options:
  153. if tcp_options[0][0] == "MSS":
  154. tcp_options[0] = ("MSS", mss_value)
  155. tcp_pkt.setfieldval("options", tcp_options)
  156. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  157. new_pkt.time = timestamp_next_pkt
  158. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  159. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[
  160. self.pkt_num] # float(timeSteps.random())
  161. # Reply
  162. else:
  163. # Ether
  164. eth_frame.setfieldval("src", mac_destination)
  165. eth_frame.setfieldval("dst", mac_source)
  166. # IP
  167. ip_pkt.setfieldval("src", ip_destination)
  168. ip_pkt.setfieldval("dst", ip_source)
  169. ip_pkt.setfieldval("ttl", destination_ttl_value)
  170. # TCP
  171. tcp_pkt.setfieldval("dport", port_source)
  172. tcp_pkt.setfieldval("sport", port_destination)
  173. # Window Size
  174. destination_origin_win = tcp_pkt.getfieldval("window")
  175. if destination_origin_win not in destination_origin_wins:
  176. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  177. new_win = destination_origin_wins[destination_origin_win]
  178. tcp_pkt.setfieldval("window", new_win)
  179. # MSS
  180. tcp_options = tcp_pkt.getfieldval("options")
  181. if tcp_options:
  182. if tcp_options[0][0] == "MSS":
  183. tcp_options[0] = ("MSS", mss_value)
  184. tcp_pkt.setfieldval("options", tcp_options)
  185. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  186. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[
  187. self.pkt_num] # + float(timeSteps.random())
  188. new_pkt.time = timestamp_next_pkt
  189. self.packets.append(new_pkt)
  190. exploit_raw_packets.close()
  191. def generate_attack_pcap(self):
  192. """
  193. Creates a pcap containing the attack packets.
  194. :return: The location of the generated pcap file.
  195. """
  196. # Store timestamp of first packet (for attack label)
  197. self.attack_start_utime = self.packets[0].time
  198. self.attack_end_utime = self.packets[-1].time
  199. if len(self.packets) > 0:
  200. self.packets = sorted(self.packets, key=lambda pkt: pkt.time)
  201. self.path_attack_pcap = self.write_attack_pcap(self.packets, True, self.path_attack_pcap)
  202. # return packets sorted by packet time_sec_start
  203. # pkt_num+1: because pkt_num starts at 0
  204. return self.pkt_num + 1, self.path_attack_pcap