EternalBlueExploit.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. import logging
  2. from random import randint, uniform
  3. from lea import Lea
  4. from Attack import BaseAttack
  5. from Attack.AttackParameters import Parameter as Param
  6. from Attack.AttackParameters import ParameterTypes
  7. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  8. # noinspection PyPep8
  9. from scapy.utils import RawPcapReader
  10. from scapy.layers.inet import IP, Ether, TCP, RandShort
  11. class EternalBlueExploit(BaseAttack.BaseAttack):
  12. template_scan_pcap_path = "resources/Win7_eternalblue_scan.pcap"
  13. template_attack_pcap_path = "resources/Win7_eternalblue_exploit.pcap"
  14. # SMB port
  15. smb_port = 445
  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. "Resource Exhaustion")
  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.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  32. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  33. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  34. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  35. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT
  36. }
  37. def init_params(self):
  38. """
  39. Initialize the parameters of this attack using the user supplied command line parameters.
  40. Use the provided statistics to calculate default parameters and to process user
  41. supplied queries.
  42. :param statistics: Reference to a statistics object.
  43. """
  44. # PARAMETERS: initialize with default utilsvalues
  45. # (values are overwritten if user specifies them)
  46. # Attacker configuration
  47. most_used_ip_address = self.statistics.get_most_used_ip_address()
  48. if isinstance(most_used_ip_address, list):
  49. most_used_ip_address = most_used_ip_address[0]
  50. random_ip_address = self.statistics.get_random_ip_address()
  51. self.add_param_value(Param.IP_SOURCE, random_ip_address)
  52. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(random_ip_address))
  53. # Victim configuration
  54. self.add_param_value(Param.IP_DESTINATION, most_used_ip_address)
  55. destination_mac = self.statistics.get_mac_address(most_used_ip_address)
  56. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  57. destination_mac = self.generate_random_mac_address()
  58. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  59. # Attack configuration
  60. self.add_param_value(Param.PACKETS_PER_SECOND,
  61. (self.statistics.get_pps_sent(most_used_ip_address) +
  62. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  63. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  64. def generate_attack_pcap(self):
  65. def update_timestamp(timestamp, pps):
  66. """
  67. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  68. :return: Timestamp to be used for the next packet.
  69. """
  70. # Calculate the request timestamp
  71. # A distribution to imitate the bursty behavior of traffic
  72. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 20, 5 / pps: 7, 10 / pps: 3})
  73. return timestamp + uniform(1 / pps, randomdelay.random())
  74. def getIntervalPPS(complement_interval_pps, timestamp):
  75. """
  76. Gets the packet rate (pps) in specific time interval.
  77. :return: the corresponding packet rate for packet rate (pps) .
  78. """
  79. for row in complement_interval_pps:
  80. if timestamp<=row[0]:
  81. return row[1]
  82. return complement_interval_pps[-1][1] # in case the timstamp > capture max timestamp
  83. # Timestamp
  84. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  85. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  86. # calculate complement packet rates of BG traffic per interval
  87. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  88. # Initialize parameters
  89. packets = []
  90. mac_source = self.get_param_value(Param.MAC_SOURCE)
  91. ip_source = self.get_param_value(Param.IP_SOURCE)
  92. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  93. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  94. # Check ip.src == ip.dst
  95. self.ip_src_dst_equal_check(ip_source, ip_destination)
  96. path_attack_pcap = None
  97. # Set TTL based on TTL distribution of IP address
  98. source_ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  99. if len(source_ttl_dist) > 0:
  100. source_ttl_prob_dict = Lea.fromValFreqsDict(source_ttl_dist)
  101. source_ttl_value = source_ttl_prob_dict.random()
  102. else:
  103. source_ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  104. destination_ttl_dist = self.statistics.get_ttl_distribution(ip_destination)
  105. if len(destination_ttl_dist) > 0:
  106. destination_ttl_prob_dict = Lea.fromValFreqsDict(destination_ttl_dist)
  107. destination_ttl_value = destination_ttl_prob_dict.random()
  108. else:
  109. destination_ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  110. # Set Window Size based on Window Size distribution of IP address
  111. source_win_dist = self.statistics.get_win_distribution(ip_source)
  112. if len(source_win_dist) > 0:
  113. source_win_prob_dict = Lea.fromValFreqsDict(source_win_dist)
  114. else:
  115. source_win_dist = self.statistics.get_win_distribution(self.statistics.get_most_used_ip_address())
  116. source_win_prob_dict = Lea.fromValFreqsDict(source_win_dist)
  117. destination_win_dist = self.statistics.get_win_distribution(ip_destination)
  118. if len(destination_win_dist) > 0:
  119. destination_win_prob_dict = Lea.fromValFreqsDict(destination_win_dist)
  120. else:
  121. destination_win_dist = self.statistics.get_win_distribution(self.statistics.get_most_used_ip_address())
  122. destination_win_prob_dict = Lea.fromValFreqsDict(destination_win_dist)
  123. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  124. mss_value = self.statistics.process_db_query("most_used(mssValue)")
  125. if not mss_value:
  126. mss_value = 1465
  127. # Scan (MS17) for EternalBlue
  128. # Read Win7_eternalblue_scan pcap file
  129. orig_ip_dst = None
  130. exploit_raw_packets = RawPcapReader(self.template_scan_pcap_path)
  131. inter_arrival_times = self.get_inter_arrival_time(exploit_raw_packets)
  132. exploit_raw_packets = RawPcapReader(self.template_scan_pcap_path)
  133. port_source = randint(self.minDefaultPort,self.maxDefaultPort) # experiments show this range of ports
  134. source_origin_wins, destination_origin_wins = {}, {}
  135. for pkt_num, pkt in enumerate(exploit_raw_packets):
  136. eth_frame = Ether(pkt[0])
  137. ip_pkt = eth_frame.payload
  138. tcp_pkt = ip_pkt.payload
  139. if pkt_num == 0:
  140. if tcp_pkt.getfieldval("dport") == self.smb_port:
  141. orig_ip_dst = ip_pkt.getfieldval("dst") # victim IP
  142. # Request
  143. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  144. # Ether
  145. eth_frame.setfieldval("src", mac_source)
  146. eth_frame.setfieldval("dst", mac_destination)
  147. # IP
  148. ip_pkt.setfieldval("src", ip_source)
  149. ip_pkt.setfieldval("dst", ip_destination)
  150. ip_pkt.setfieldval("ttl", source_ttl_value)
  151. # TCP
  152. tcp_pkt.setfieldval("sport",port_source)
  153. ## Window Size
  154. source_origin_win = tcp_pkt.getfieldval("window")
  155. if source_origin_win not in source_origin_wins:
  156. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  157. new_win = source_origin_wins[source_origin_win]
  158. tcp_pkt.setfieldval("window", new_win)
  159. ## MSS
  160. tcp_options = tcp_pkt.getfieldval("options")
  161. if tcp_options:
  162. if tcp_options[0][0] == "MSS":
  163. tcp_options [0] = ("MSS",mss_value)
  164. tcp_pkt.setfieldval("options", tcp_options)
  165. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  166. new_pkt.time = timestamp_next_pkt
  167. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  168. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]#float(timeSteps.random())
  169. # Reply
  170. else:
  171. # Ether
  172. eth_frame.setfieldval("src", mac_destination)
  173. eth_frame.setfieldval("dst", mac_source)
  174. # IP
  175. ip_pkt.setfieldval("src", ip_destination)
  176. ip_pkt.setfieldval("dst", ip_source)
  177. ip_pkt.setfieldval("ttl", destination_ttl_value)
  178. # TCP
  179. tcp_pkt.setfieldval("dport", port_source)
  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. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]#+ float(timeSteps.random())
  194. new_pkt.time = timestamp_next_pkt
  195. packets.append(new_pkt)
  196. # Inject EternalBlue exploit packets
  197. # Read Win7_eternalblue_exploit pcap file
  198. exploit_raw_packets = RawPcapReader(self.template_attack_pcap_path)
  199. port_source = randint(self.minDefaultPort,self.maxDefaultPort) # experiments show this range of ports
  200. # conversations = {(ip.src, ip.dst, port.src, port.dst): packets}
  201. conversations, orderList_conversations = self.packetsToConvs(exploit_raw_packets)
  202. conv_start_timesamp = timestamp_next_pkt
  203. for conv_index, conv in enumerate(orderList_conversations):
  204. conv_start_timesamp = conv_start_timesamp + uniform(0.001,0.01) # the distance between the starts of the converstaions
  205. timestamp_next_pkt = conv_start_timesamp
  206. conv_pkts = conversations[conv]
  207. inter_arrival_times = self.get_inter_arrival_time(conv_pkts)
  208. if conv_index == len(orderList_conversations) - 2: # Not the last conversation
  209. timestamp_next_pkt = packets[-1].time + uniform(0.001,0.01)
  210. if conv_index != len(orderList_conversations)-1: # Not the last conversation
  211. port_source += 2
  212. for pkt_num, pkt in enumerate(conv_pkts):
  213. eth_frame = Ether(pkt[0])
  214. ip_pkt = eth_frame.payload
  215. tcp_pkt = ip_pkt.payload
  216. if pkt_num == 0:
  217. if tcp_pkt.getfieldval("dport") == self.smb_port:
  218. orig_ip_dst = ip_pkt.getfieldval("dst")
  219. # Request
  220. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  221. # Ether
  222. eth_frame.setfieldval("src", mac_source)
  223. eth_frame.setfieldval("dst", mac_destination)
  224. # IP
  225. ip_pkt.setfieldval("src", ip_source)
  226. ip_pkt.setfieldval("dst", ip_destination)
  227. ip_pkt.setfieldval("ttl", source_ttl_value)
  228. # TCP
  229. tcp_pkt.setfieldval("sport", port_source)
  230. ## Window Size
  231. source_origin_win = tcp_pkt.getfieldval("window")
  232. if source_origin_win not in source_origin_wins:
  233. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  234. new_win = source_origin_wins[source_origin_win]
  235. tcp_pkt.setfieldval("window", new_win)
  236. ## MSS
  237. tcp_options = tcp_pkt.getfieldval("options")
  238. if tcp_options:
  239. if tcp_options[0][0] == "MSS":
  240. tcp_options[0] = ("MSS", mss_value)
  241. tcp_pkt.setfieldval("options", tcp_options)
  242. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  243. new_pkt.time = timestamp_next_pkt
  244. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  245. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num] #float(timeSteps.random())
  246. # Reply
  247. else:
  248. # Ether
  249. eth_frame.setfieldval("src", mac_destination)
  250. eth_frame.setfieldval("dst", mac_source)
  251. # IP
  252. ip_pkt.setfieldval("src", ip_destination)
  253. ip_pkt.setfieldval("dst", ip_source)
  254. ip_pkt.setfieldval("ttl", destination_ttl_value)
  255. # TCP
  256. tcp_pkt.setfieldval("dport", port_source)
  257. ## Window Size
  258. destination_origin_win = tcp_pkt.getfieldval("window")
  259. if destination_origin_win not in destination_origin_wins:
  260. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  261. new_win = destination_origin_wins[destination_origin_win]
  262. tcp_pkt.setfieldval("window", new_win)
  263. ## MSS
  264. tcp_options = tcp_pkt.getfieldval("options")
  265. if tcp_options:
  266. if tcp_options[0][0] == "MSS":
  267. tcp_options[0] = ("MSS", mss_value)
  268. tcp_pkt.setfieldval("options", tcp_options)
  269. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  270. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  271. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]#float(timeSteps.random())
  272. new_pkt.time = timestamp_next_pkt
  273. packets.append(new_pkt)
  274. else: # Last conversation where the victim start a connection with the attacker
  275. timestamp_next_pkt = packets[-1].time + uniform(0.001, 0.01)
  276. port_source = randint(self.minDefaultPort,self.maxDefaultPort)
  277. for pkt_num, pkt in enumerate(conv_pkts):
  278. eth_frame = Ether(pkt[0])
  279. ip_pkt = eth_frame.payload
  280. tcp_pkt = ip_pkt.payload
  281. # Request
  282. if tcp_pkt.getfieldval("dport") == self.last_conn_dst_port:
  283. # Ether
  284. eth_frame.setfieldval("src", mac_destination)
  285. eth_frame.setfieldval("dst", mac_source)
  286. # IP
  287. ip_pkt.setfieldval("src", ip_destination)
  288. ip_pkt.setfieldval("dst", ip_source)
  289. ip_pkt.setfieldval("ttl", destination_ttl_value)
  290. # TCP
  291. tcp_pkt.setfieldval("sport", port_source)
  292. ## Window Size
  293. destination_origin_win = tcp_pkt.getfieldval("window")
  294. if destination_origin_win not in destination_origin_wins:
  295. destination_origin_wins[destination_origin_win] = destination_win_prob_dict.random()
  296. new_win = destination_origin_wins[destination_origin_win]
  297. tcp_pkt.setfieldval("window", new_win)
  298. ## MSS
  299. tcp_options = tcp_pkt.getfieldval("options")
  300. if tcp_options:
  301. if tcp_options[0][0] == "MSS":
  302. tcp_options[0] = ("MSS", mss_value)
  303. tcp_pkt.setfieldval("options", tcp_options)
  304. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  305. new_pkt.time = timestamp_next_pkt
  306. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  307. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]# float(timeSteps.random())
  308. # Reply
  309. else:
  310. # Ether
  311. eth_frame.setfieldval("src", mac_source)
  312. eth_frame.setfieldval("dst", mac_destination)
  313. # IP
  314. ip_pkt.setfieldval("src", ip_source)
  315. ip_pkt.setfieldval("dst", ip_destination)
  316. ip_pkt.setfieldval("ttl", source_ttl_value)
  317. # TCP
  318. tcp_pkt.setfieldval("dport", port_source)
  319. ## Window Size
  320. source_origin_win = tcp_pkt.getfieldval("window")
  321. if source_origin_win not in source_origin_wins:
  322. source_origin_wins[source_origin_win] = source_win_prob_dict.random()
  323. new_win = source_origin_wins[source_origin_win]
  324. tcp_pkt.setfieldval("window", new_win)
  325. ## MSS
  326. tcp_options = tcp_pkt.getfieldval("options")
  327. if tcp_options:
  328. if tcp_options[0][0] == "MSS":
  329. tcp_options[0] = ("MSS", mss_value)
  330. tcp_pkt.setfieldval("options", tcp_options)
  331. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  332. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  333. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + inter_arrival_times[pkt_num]# float(timeSteps.random())
  334. new_pkt.time = timestamp_next_pkt
  335. packets.append(new_pkt)
  336. # Store timestamp of first packet (for attack label)
  337. self.attack_start_utime = packets[0].time
  338. self.attack_end_utime = packets[-1].time
  339. if len(packets) > 0:
  340. packets = sorted(packets, key=lambda pkt: pkt.time)
  341. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  342. # return packets sorted by packet time_sec_start
  343. # pkt_num+1: because pkt_num starts at 0
  344. return pkt_num + 1, path_attack_pcap