EternalBlueExploit.py 21 KB

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