EternalBlueExploit.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. import logging
  2. from random import randint, uniform
  3. from lea import Lea
  4. from scapy.utils import RawPcapReader
  5. from scapy.layers.inet import Ether
  6. from Attack import BaseAttack
  7. from Attack.AttackParameters import Parameter as Param
  8. from Attack.AttackParameters import ParameterTypes
  9. from ID2TLib.Utility import update_timestamp, get_interval_pps
  10. from ID2TLib.SMBLib import smb_port
  11. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  12. # noinspection PyPep8
  13. class EternalBlueExploit(BaseAttack.BaseAttack):
  14. template_scan_pcap_path = "resources/Win7_eternalblue_scan.pcap"
  15. template_attack_pcap_path = "resources/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 = {
  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. if isinstance(most_used_ip_address, list):
  51. most_used_ip_address = most_used_ip_address[0]
  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_pcap(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. 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. 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 = 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 = 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 = self.statistics.process_db_query("most_used(mssValue)")
  113. if not mss_value:
  114. mss_value = 1465
  115. # Scan (MS17) for EternalBlue
  116. # Read Win7_eternalblue_scan pcap file
  117. orig_ip_dst = None
  118. exploit_raw_packets = RawPcapReader(self.template_scan_pcap_path)
  119. inter_arrival_times = self.get_inter_arrival_time(exploit_raw_packets)
  120. exploit_raw_packets = RawPcapReader(self.template_scan_pcap_path)
  121. source_origin_wins, destination_origin_wins = {}, {}
  122. for pkt_num, pkt in enumerate(exploit_raw_packets):
  123. eth_frame = Ether(pkt[0])
  124. ip_pkt = eth_frame.payload
  125. tcp_pkt = ip_pkt.payload
  126. if pkt_num == 0:
  127. if tcp_pkt.getfieldval("dport") == smb_port:
  128. orig_ip_dst = ip_pkt.getfieldval("dst") # victim IP
  129. # Request
  130. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  131. # Ether
  132. eth_frame.setfieldval("src", mac_source)
  133. eth_frame.setfieldval("dst", mac_destination)
  134. # IP
  135. ip_pkt.setfieldval("src", ip_source)
  136. ip_pkt.setfieldval("dst", ip_destination)
  137. ip_pkt.setfieldval("ttl", source_ttl_value)
  138. # TCP
  139. tcp_pkt.setfieldval("sport",port_source)
  140. tcp_pkt.setfieldval("dport",port_destination)
  141. ## Window Size (mapping)
  142. source_origin_win = tcp_pkt.getfieldval("window")
  143. if source_origin_win not in source_origin_wins:
  144. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  145. new_win = source_origin_wins[source_origin_win]
  146. tcp_pkt.setfieldval("window", new_win)
  147. ## MSS
  148. tcp_options = tcp_pkt.getfieldval("options")
  149. if tcp_options:
  150. if tcp_options[0][0] == "MSS":
  151. tcp_options [0] = ("MSS",mss_value)
  152. tcp_pkt.setfieldval("options", tcp_options)
  153. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  154. new_pkt.time = timestamp_next_pkt
  155. pps = max(get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  156. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]#float(timeSteps.random())
  157. # Reply
  158. else:
  159. # Ether
  160. eth_frame.setfieldval("src", mac_destination)
  161. eth_frame.setfieldval("dst", mac_source)
  162. # IP
  163. ip_pkt.setfieldval("src", ip_destination)
  164. ip_pkt.setfieldval("dst", ip_source)
  165. ip_pkt.setfieldval("ttl", destination_ttl_value)
  166. # TCP
  167. tcp_pkt.setfieldval("dport", port_source)
  168. tcp_pkt.setfieldval("sport",port_destination)
  169. ## Window Size
  170. destination_origin_win = tcp_pkt.getfieldval("window")
  171. if destination_origin_win not in destination_origin_wins:
  172. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  173. new_win = destination_origin_wins[destination_origin_win]
  174. tcp_pkt.setfieldval("window", new_win)
  175. ## MSS
  176. tcp_options = tcp_pkt.getfieldval("options")
  177. if tcp_options:
  178. if tcp_options[0][0] == "MSS":
  179. tcp_options[0] = ("MSS", mss_value)
  180. tcp_pkt.setfieldval("options", tcp_options)
  181. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  182. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]#+ float(timeSteps.random())
  183. new_pkt.time = timestamp_next_pkt
  184. packets.append(new_pkt)
  185. # Inject EternalBlue exploit packets
  186. # Read Win7_eternalblue_exploit pcap file
  187. exploit_raw_packets = RawPcapReader(self.template_attack_pcap_path)
  188. port_source = randint(self.minDefaultPort,self.maxDefaultPort) # experiments show this range of ports
  189. # conversations = {(ip.src, ip.dst, port.src, port.dst): packets}
  190. conversations, orderList_conversations = self.packetsToConvs(exploit_raw_packets)
  191. conv_start_timesamp = timestamp_next_pkt
  192. for conv_index, conv in enumerate(orderList_conversations):
  193. conv_start_timesamp = conv_start_timesamp + uniform(0.001,0.01) # the distance between the starts of the converstaions
  194. timestamp_next_pkt = conv_start_timesamp
  195. conv_pkts = conversations[conv]
  196. inter_arrival_times = self.get_inter_arrival_time(conv_pkts)
  197. if conv_index == len(orderList_conversations) - 2: # Not the last conversation
  198. timestamp_next_pkt = packets[-1].time + uniform(0.001,0.01)
  199. if conv_index != len(orderList_conversations)-1: # Not the last conversation
  200. port_source += 2
  201. for pkt_num, pkt in enumerate(conv_pkts):
  202. eth_frame = Ether(pkt[0])
  203. ip_pkt = eth_frame.payload
  204. tcp_pkt = ip_pkt.payload
  205. if pkt_num == 0:
  206. if tcp_pkt.getfieldval("dport") == smb_port:
  207. orig_ip_dst = ip_pkt.getfieldval("dst")
  208. # Request
  209. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  210. # Ether
  211. eth_frame.setfieldval("src", mac_source)
  212. eth_frame.setfieldval("dst", mac_destination)
  213. # IP
  214. ip_pkt.setfieldval("src", ip_source)
  215. ip_pkt.setfieldval("dst", ip_destination)
  216. ip_pkt.setfieldval("ttl", source_ttl_value)
  217. # TCP
  218. tcp_pkt.setfieldval("sport", port_source)
  219. tcp_pkt.setfieldval("dport", port_destination)
  220. ## Window Size
  221. source_origin_win = tcp_pkt.getfieldval("window")
  222. if source_origin_win not in source_origin_wins:
  223. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  224. new_win = source_origin_wins[source_origin_win]
  225. tcp_pkt.setfieldval("window", new_win)
  226. ## MSS
  227. tcp_options = tcp_pkt.getfieldval("options")
  228. if tcp_options:
  229. if tcp_options[0][0] == "MSS":
  230. tcp_options[0] = ("MSS", mss_value)
  231. tcp_pkt.setfieldval("options", tcp_options)
  232. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  233. new_pkt.time = timestamp_next_pkt
  234. pps = max(get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  235. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num] #float(timeSteps.random())
  236. # Reply
  237. else:
  238. # Ether
  239. eth_frame.setfieldval("src", mac_destination)
  240. eth_frame.setfieldval("dst", mac_source)
  241. # IP
  242. ip_pkt.setfieldval("src", ip_destination)
  243. ip_pkt.setfieldval("dst", ip_source)
  244. ip_pkt.setfieldval("ttl", destination_ttl_value)
  245. # TCP
  246. tcp_pkt.setfieldval("dport", port_source)
  247. tcp_pkt.setfieldval("sport", port_destination)
  248. ## Window Size
  249. destination_origin_win = tcp_pkt.getfieldval("window")
  250. if destination_origin_win not in destination_origin_wins:
  251. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  252. new_win = destination_origin_wins[destination_origin_win]
  253. tcp_pkt.setfieldval("window", new_win)
  254. ## MSS
  255. tcp_options = tcp_pkt.getfieldval("options")
  256. if tcp_options:
  257. if tcp_options[0][0] == "MSS":
  258. tcp_options[0] = ("MSS", mss_value)
  259. tcp_pkt.setfieldval("options", tcp_options)
  260. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  261. pps = max(get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  262. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]#float(timeSteps.random())
  263. new_pkt.time = timestamp_next_pkt
  264. packets.append(new_pkt)
  265. else: # Last conversation where the victim start a connection with the attacker
  266. timestamp_next_pkt = packets[-1].time + uniform(0.001, 0.01)
  267. port_source = randint(self.minDefaultPort,self.maxDefaultPort)
  268. for pkt_num, pkt in enumerate(conv_pkts):
  269. eth_frame = Ether(pkt[0])
  270. ip_pkt = eth_frame.payload
  271. tcp_pkt = ip_pkt.payload
  272. # Request
  273. if tcp_pkt.getfieldval("dport") == self.last_conn_dst_port:
  274. # Ether
  275. eth_frame.setfieldval("src", mac_destination)
  276. eth_frame.setfieldval("dst", mac_source)
  277. # IP
  278. ip_pkt.setfieldval("src", ip_destination)
  279. ip_pkt.setfieldval("dst", ip_source)
  280. ip_pkt.setfieldval("ttl", destination_ttl_value)
  281. # TCP
  282. tcp_pkt.setfieldval("sport", port_source)
  283. # destination port is fixed 4444
  284. ## Window Size
  285. destination_origin_win = tcp_pkt.getfieldval("window")
  286. if destination_origin_win not in destination_origin_wins:
  287. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  288. new_win = destination_origin_wins[destination_origin_win]
  289. tcp_pkt.setfieldval("window", new_win)
  290. ## MSS
  291. tcp_options = tcp_pkt.getfieldval("options")
  292. if tcp_options:
  293. if tcp_options[0][0] == "MSS":
  294. tcp_options[0] = ("MSS", mss_value)
  295. tcp_pkt.setfieldval("options", tcp_options)
  296. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  297. new_pkt.time = timestamp_next_pkt
  298. pps = max(get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  299. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]# float(timeSteps.random())
  300. # Reply
  301. else:
  302. # Ether
  303. eth_frame.setfieldval("src", mac_source)
  304. eth_frame.setfieldval("dst", mac_destination)
  305. # IP
  306. ip_pkt.setfieldval("src", ip_source)
  307. ip_pkt.setfieldval("dst", ip_destination)
  308. ip_pkt.setfieldval("ttl", source_ttl_value)
  309. # TCP
  310. tcp_pkt.setfieldval("dport", port_source)
  311. # source port is fixed 4444
  312. ## Window Size
  313. source_origin_win = tcp_pkt.getfieldval("window")
  314. if source_origin_win not in source_origin_wins:
  315. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  316. new_win = source_origin_wins[source_origin_win]
  317. tcp_pkt.setfieldval("window", new_win)
  318. ## MSS
  319. tcp_options = tcp_pkt.getfieldval("options")
  320. if tcp_options:
  321. if tcp_options[0][0] == "MSS":
  322. tcp_options[0] = ("MSS", mss_value)
  323. tcp_pkt.setfieldval("options", tcp_options)
  324. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  325. pps = max(get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  326. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]# float(timeSteps.random())
  327. new_pkt.time = timestamp_next_pkt
  328. packets.append(new_pkt)
  329. # Store timestamp of first packet (for attack label)
  330. self.attack_start_utime = packets[0].time
  331. self.attack_end_utime = packets[-1].time
  332. if len(packets) > 0:
  333. packets = sorted(packets, key=lambda pkt: pkt.time)
  334. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  335. # return packets sorted by packet time_sec_start
  336. # pkt_num+1: because pkt_num starts at 0
  337. return pkt_num + 1, path_attack_pcap