EternalBlueExploit.py 17 KB

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