EternalBlueExploit.py 20 KB

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