EternalBlueExploit.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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 EternalBlueExploit(BaseAttack.BaseAttack):
  13. template_scan_pcap_path = Util.RESOURCE_DIR + "Win7_eternalblue_scan.pcap"
  14. template_attack_pcap_path = Util.RESOURCE_DIR + "Win7_eternalblue_exploit.pcap"
  15. # Empirical values from Metasploit experiments
  16. minDefaultPort = 30000
  17. maxDefaultPort = 50000
  18. last_conn_dst_port = 4444
  19. def __init__(self):
  20. """
  21. Creates a new instance of the EternalBlue Exploit.
  22. """
  23. # Initialize attack
  24. super(EternalBlueExploit, self).__init__("EternalBlue Exploit", "Injects an EternalBlue exploit'",
  25. "Privilege elevation")
  26. self.pkt_num = 0
  27. self.path_attack_pcap = None
  28. # Define allowed parameters and their type
  29. self.supported_params.update({
  30. atkParam.Parameter.MAC_SOURCE: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  31. atkParam.Parameter.IP_SOURCE: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  32. atkParam.Parameter.PORT_SOURCE: atkParam.ParameterTypes.TYPE_PORT,
  33. atkParam.Parameter.MAC_DESTINATION: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  34. atkParam.Parameter.IP_DESTINATION: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  35. atkParam.Parameter.PORT_DESTINATION: atkParam.ParameterTypes.TYPE_PORT,
  36. atkParam.Parameter.INJECT_AT_TIMESTAMP: atkParam.ParameterTypes.TYPE_FLOAT,
  37. atkParam.Parameter.INJECT_AFTER_PACKET: atkParam.ParameterTypes.TYPE_PACKET_POSITION,
  38. atkParam.Parameter.PACKET_LIMIT_PER_SECOND: atkParam.ParameterTypes.TYPE_FLOAT
  39. })
  40. def init_params(self):
  41. """
  42. Initialize the parameters of this attack using the user supplied command line parameters.
  43. Use the provided statistics to calculate default parameters and to process user
  44. supplied queries.
  45. """
  46. # PARAMETERS: initialize with default utilsvalues
  47. # (values are overwritten if user specifies them)
  48. # Attacker configuration
  49. most_used_ip_address = self.statistics.get_most_used_ip_address()
  50. random_ip_address = self.statistics.get_random_ip_address()
  51. while random_ip_address == most_used_ip_address:
  52. random_ip_address = self.statistics.get_random_ip_address()
  53. self.add_param_value(atkParam.Parameter.IP_SOURCE, random_ip_address)
  54. self.add_param_value(atkParam.Parameter.MAC_SOURCE, self.statistics.get_mac_address(random_ip_address))
  55. self.add_param_value(atkParam.Parameter.PORT_SOURCE, rnd.randint(self.minDefaultPort, self.maxDefaultPort))
  56. # Victim configuration
  57. self.add_param_value(atkParam.Parameter.IP_DESTINATION, most_used_ip_address)
  58. destination_mac = self.statistics.get_mac_address(most_used_ip_address)
  59. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  60. destination_mac = self.generate_random_mac_address()
  61. self.add_param_value(atkParam.Parameter.MAC_DESTINATION, destination_mac)
  62. self.add_param_value(atkParam.Parameter.PORT_DESTINATION, SMBLib.smb_port)
  63. # Attack configuration
  64. self.add_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND,
  65. (self.statistics.get_pps_sent(most_used_ip_address) +
  66. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  67. self.add_param_value(atkParam.Parameter.INJECT_AFTER_PACKET, rnd.randint(0, self.statistics.get_packet_count()))
  68. def generate_attack_packets(self):
  69. """
  70. Creates the attack packets.
  71. """
  72. # Timestamp
  73. timestamp_next_pkt = self.get_param_value(atkParam.Parameter.INJECT_AT_TIMESTAMP)
  74. pps = self.get_param_value(atkParam.Parameter.PACKET_LIMIT_PER_SECOND)
  75. # calculate complement packet rates of BG traffic per interval
  76. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  77. # Initialize parameters
  78. self.packets = []
  79. mac_source = self.get_param_value(atkParam.Parameter.MAC_SOURCE)
  80. ip_source = self.get_param_value(atkParam.Parameter.IP_SOURCE)
  81. # FIXME: why is port_source never used?
  82. port_source = self.get_param_value(atkParam.Parameter.PORT_SOURCE)
  83. mac_destination = self.get_param_value(atkParam.Parameter.MAC_DESTINATION)
  84. ip_destination = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  85. port_destination = self.get_param_value(atkParam.Parameter.PORT_DESTINATION)
  86. # Check ip.src == ip.dst
  87. self.ip_src_dst_equal_check(ip_source, ip_destination)
  88. # Set TTL based on TTL distribution of IP address
  89. source_ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  90. if len(source_ttl_dist) > 0:
  91. source_ttl_prob_dict = lea.Lea.fromValFreqsDict(source_ttl_dist)
  92. source_ttl_value = source_ttl_prob_dict.random()
  93. else:
  94. source_ttl_value = Util.handle_most_used_outputs(self.statistics.get_most_used_ttl_value())
  95. destination_ttl_dist = self.statistics.get_ttl_distribution(ip_destination)
  96. if len(destination_ttl_dist) > 0:
  97. destination_ttl_prob_dict = lea.Lea.fromValFreqsDict(destination_ttl_dist)
  98. destination_ttl_value = destination_ttl_prob_dict.random()
  99. else:
  100. destination_ttl_value = Util.handle_most_used_outputs(
  101. self.statistics.get_most_used_ttl_value())
  102. # Set Window Size based on Window Size distribution of IP address
  103. source_win_dist = self.statistics.get_win_distribution(ip_source)
  104. if len(source_win_dist) > 0:
  105. source_win_prob_dict = lea.Lea.fromValFreqsDict(source_win_dist)
  106. else:
  107. source_win_dist = self.statistics.get_win_distribution(self.statistics.get_most_used_ip_address())
  108. source_win_prob_dict = lea.Lea.fromValFreqsDict(source_win_dist)
  109. destination_win_dist = self.statistics.get_win_distribution(ip_destination)
  110. if len(destination_win_dist) > 0:
  111. destination_win_prob_dict = lea.Lea.fromValFreqsDict(destination_win_dist)
  112. else:
  113. destination_win_dist = self.statistics.get_win_distribution(self.statistics.get_most_used_ip_address())
  114. destination_win_prob_dict = lea.Lea.fromValFreqsDict(destination_win_dist)
  115. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  116. mss_value = Util.handle_most_used_outputs(self.statistics.get_most_used_mss_value())
  117. if not mss_value:
  118. mss_value = 1465
  119. # Inject EternalBlue exploit packets
  120. # Read Win7_eternalblue_exploit pcap file
  121. source_origin_wins, destination_origin_wins = {}, {}
  122. exploit_raw_packets = scapy.utils.RawPcapReader(self.template_attack_pcap_path)
  123. port_source = rnd.randint(self.minDefaultPort, self.maxDefaultPort) # experiments show this range of ports
  124. # conversations = {(ip.src, ip.dst, port.src, port.dst): packets}
  125. conversations, order_list_conversations = self.packets_to_convs(exploit_raw_packets)
  126. exploit_raw_packets.close()
  127. conv_start_timesamp = timestamp_next_pkt
  128. for conv_index, conv in enumerate(order_list_conversations):
  129. # the distance between the starts of the converstaions
  130. conv_start_timesamp = conv_start_timesamp + rnd.uniform(0.001, 0.01)
  131. timestamp_next_pkt = conv_start_timesamp
  132. conv_pkts = conversations[conv]
  133. inter_arrival_times = self.get_inter_arrival_time(conv_pkts)
  134. if conv_index == len(order_list_conversations) - 2: # Not the last conversation
  135. timestamp_next_pkt = self.packets[-1].time + rnd.uniform(0.001, 0.01)
  136. if conv_index != len(order_list_conversations) - 1: # Not the last conversation
  137. port_source += 2
  138. for self.pkt_num, pkt in enumerate(conv_pkts):
  139. eth_frame = inet.Ether(pkt[0])
  140. ip_pkt = eth_frame.payload
  141. tcp_pkt = ip_pkt.payload
  142. if self.pkt_num == 0:
  143. if tcp_pkt.getfieldval("dport") == SMBLib.smb_port:
  144. orig_ip_dst = ip_pkt.getfieldval("dst")
  145. # Request
  146. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  147. # Ether
  148. eth_frame.setfieldval("src", mac_source)
  149. eth_frame.setfieldval("dst", mac_destination)
  150. # IP
  151. ip_pkt.setfieldval("src", ip_source)
  152. ip_pkt.setfieldval("dst", ip_destination)
  153. ip_pkt.setfieldval("ttl", source_ttl_value)
  154. # TCP
  155. tcp_pkt.setfieldval("sport", port_source)
  156. tcp_pkt.setfieldval("dport", port_destination)
  157. # Window Size
  158. source_origin_win = tcp_pkt.getfieldval("window")
  159. if source_origin_win not in source_origin_wins:
  160. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  161. new_win = source_origin_wins[source_origin_win]
  162. tcp_pkt.setfieldval("window", new_win)
  163. # MSS
  164. tcp_options = tcp_pkt.getfieldval("options")
  165. if tcp_options:
  166. if tcp_options[0][0] == "MSS":
  167. tcp_options[0] = ("MSS", mss_value)
  168. tcp_pkt.setfieldval("options", tcp_options)
  169. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  170. new_pkt.time = timestamp_next_pkt
  171. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  172. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[
  173. self.pkt_num] # float(timeSteps.random())
  174. # Reply
  175. else:
  176. # Ether
  177. eth_frame.setfieldval("src", mac_destination)
  178. eth_frame.setfieldval("dst", mac_source)
  179. # IP
  180. ip_pkt.setfieldval("src", ip_destination)
  181. ip_pkt.setfieldval("dst", ip_source)
  182. ip_pkt.setfieldval("ttl", destination_ttl_value)
  183. # TCP
  184. tcp_pkt.setfieldval("dport", port_source)
  185. tcp_pkt.setfieldval("sport", port_destination)
  186. # Window Size
  187. destination_origin_win = tcp_pkt.getfieldval("window")
  188. if destination_origin_win not in destination_origin_wins:
  189. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  190. new_win = destination_origin_wins[destination_origin_win]
  191. tcp_pkt.setfieldval("window", new_win)
  192. # MSS
  193. tcp_options = tcp_pkt.getfieldval("options")
  194. if tcp_options:
  195. if tcp_options[0][0] == "MSS":
  196. tcp_options[0] = ("MSS", mss_value)
  197. tcp_pkt.setfieldval("options", tcp_options)
  198. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  199. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  200. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[
  201. self.pkt_num] # float(timeSteps.random())
  202. new_pkt.time = timestamp_next_pkt
  203. self.packets.append(new_pkt)
  204. else: # Last conversation where the victim start a connection with the attacker
  205. timestamp_next_pkt = self.packets[-1].time + rnd.uniform(0.001, 0.01)
  206. port_source = rnd.randint(self.minDefaultPort, self.maxDefaultPort)
  207. for self.pkt_num, pkt in enumerate(conv_pkts):
  208. eth_frame = inet.Ether(pkt[0])
  209. ip_pkt = eth_frame.payload
  210. tcp_pkt = ip_pkt.payload
  211. # Request
  212. if tcp_pkt.getfieldval("dport") == self.last_conn_dst_port:
  213. # Ether
  214. eth_frame.setfieldval("src", mac_destination)
  215. eth_frame.setfieldval("dst", mac_source)
  216. # IP
  217. ip_pkt.setfieldval("src", ip_destination)
  218. ip_pkt.setfieldval("dst", ip_source)
  219. ip_pkt.setfieldval("ttl", destination_ttl_value)
  220. # TCP
  221. tcp_pkt.setfieldval("sport", port_source)
  222. # destination port is fixed 4444
  223. # Window Size
  224. destination_origin_win = tcp_pkt.getfieldval("window")
  225. if destination_origin_win not in destination_origin_wins:
  226. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  227. new_win = destination_origin_wins[destination_origin_win]
  228. tcp_pkt.setfieldval("window", new_win)
  229. # MSS
  230. tcp_options = tcp_pkt.getfieldval("options")
  231. if tcp_options:
  232. if tcp_options[0][0] == "MSS":
  233. tcp_options[0] = ("MSS", mss_value)
  234. tcp_pkt.setfieldval("options", tcp_options)
  235. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  236. new_pkt.time = timestamp_next_pkt
  237. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  238. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[
  239. self.pkt_num] # float(timeSteps.random())
  240. # Reply
  241. else:
  242. # Ether
  243. eth_frame.setfieldval("src", mac_source)
  244. eth_frame.setfieldval("dst", mac_destination)
  245. # IP
  246. ip_pkt.setfieldval("src", ip_source)
  247. ip_pkt.setfieldval("dst", ip_destination)
  248. ip_pkt.setfieldval("ttl", source_ttl_value)
  249. # TCP
  250. tcp_pkt.setfieldval("dport", port_source)
  251. # source port is fixed 4444
  252. # Window Size
  253. source_origin_win = tcp_pkt.getfieldval("window")
  254. if source_origin_win not in source_origin_wins:
  255. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  256. new_win = source_origin_wins[source_origin_win]
  257. tcp_pkt.setfieldval("window", new_win)
  258. # MSS
  259. tcp_options = tcp_pkt.getfieldval("options")
  260. if tcp_options:
  261. if tcp_options[0][0] == "MSS":
  262. tcp_options[0] = ("MSS", mss_value)
  263. tcp_pkt.setfieldval("options", tcp_options)
  264. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  265. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  266. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[
  267. self.pkt_num] # float(timeSteps.random())
  268. new_pkt.time = timestamp_next_pkt
  269. self.packets.append(new_pkt)
  270. def generate_attack_pcap(self):
  271. """
  272. Creates a pcap containing the attack packets.
  273. :return: The location of the generated pcap file.
  274. """
  275. # Store timestamp of first packet (for attack label)
  276. self.attack_start_utime = self.packets[0].time
  277. self.attack_end_utime = self.packets[-1].time
  278. if len(self.packets) > 0:
  279. self.packets = sorted(self.packets, key=lambda pkt: pkt.time)
  280. self.path_attack_pcap = self.write_attack_pcap(self.packets, True, self.path_attack_pcap)
  281. # return packets sorted by packet time_sec_start
  282. # pkt_num+1: because pkt_num starts at 0
  283. return self.pkt_num + 1, self.path_attack_pcap