EternalBlueExploit.py 16 KB

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