EternalBlueExploit.py 20 KB

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