EternalBlueExploit.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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_attack_pcap_path = "resources/Win7_eternalblue_scan.pcap"
  13. # SMB port
  14. smb_port = 445
  15. # Empirical values from Metasploit experiments
  16. minDefaultPort = 30000
  17. maxDefaultPort = 50000
  18. last_conn_dst_port = 4444
  19. def __init__(self, statistics, pcap_file_path):
  20. """
  21. Creates a new instance of the EternalBlue Exploit.
  22. :param statistics: A reference to the statistics class.
  23. """
  24. # Initialize attack
  25. super(EternalBlueExploit, self).__init__(statistics, "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. # PARAMETERS: initialize with default utilsvalues
  38. # (values are overwritten if user specifies them)
  39. most_used_ip_address = self.statistics.get_most_used_ip_address()
  40. if isinstance(most_used_ip_address, list):
  41. most_used_ip_address = most_used_ip_address[0]
  42. self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
  43. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  44. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  45. self.add_param_value(Param.PACKETS_PER_SECOND,
  46. (self.statistics.get_pps_sent(most_used_ip_address) +
  47. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  48. # victim configuration
  49. random_ip_address = self.statistics.get_random_ip_address()
  50. self.add_param_value(Param.IP_DESTINATION, random_ip_address)
  51. destination_mac = self.statistics.get_mac_address(random_ip_address)
  52. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  53. destination_mac = self.generate_random_mac_address()
  54. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  55. def generate_attack_pcap(self):
  56. def update_timestamp(timestamp, pps):
  57. """
  58. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  59. :return: Timestamp to be used for the next packet.
  60. """
  61. # Calculate the request timestamp
  62. # A distribution to imitate the bursty behavior of traffic
  63. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 20, 5 / pps: 7, 10 / pps: 3})
  64. return timestamp + uniform(1 / pps, randomdelay.random())
  65. def getIntervalPPS(complement_interval_pps, timestamp):
  66. """
  67. Gets the packet rate (pps) in specific time interval.
  68. :return: the corresponding packet rate for packet rate (pps) .
  69. """
  70. for row in complement_interval_pps:
  71. if timestamp<=row[0]:
  72. return row[1]
  73. return complement_interval_pps[-1][1] # in case the timstamp > capture max timestamp
  74. # Timestamp
  75. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  76. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  77. # calculate complement packet rates of BG traffic per interval
  78. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  79. # Initialize parameters
  80. packets = []
  81. mac_source = self.get_param_value(Param.MAC_SOURCE)
  82. ip_source = self.get_param_value(Param.IP_SOURCE)
  83. mac_destination = self.get_param_value(Param.MAC_DESTINATION)
  84. ip_destination = self.get_param_value(Param.IP_DESTINATION)
  85. # check ip.src == ip.dst
  86. self.ip_src_dst_equal_check(ip_source, ip_destination)
  87. path_attack_pcap = None
  88. minDelay, maxDelay = self.get_reply_delay(ip_destination)
  89. # Set TTL based on TTL distribution of IP address
  90. source_ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  91. if len(source_ttl_dist) > 0:
  92. source_ttl_prob_dict = Lea.fromValFreqsDict(source_ttl_dist)
  93. source_ttl_value = source_ttl_prob_dict.random()
  94. else:
  95. source_ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  96. destination_ttl_dist = self.statistics.get_ttl_distribution(ip_destination)
  97. if len(destination_ttl_dist) > 0:
  98. destination_ttl_prob_dict = Lea.fromValFreqsDict(destination_ttl_dist)
  99. destination_ttl_value = destination_ttl_prob_dict.random()
  100. else:
  101. destination_ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  102. # Scan (MS17) for EternalBlue
  103. # Read Win7_eternalblue_scan_vulnerable pcap file
  104. orig_ip_dst = None
  105. exploit_raw_packets = RawPcapReader(self.template_attack_pcap_path)
  106. port_source = randint(self.minDefaultPort,self.maxDefaultPort) # experiments show this range of ports
  107. for pkt_num, pkt in enumerate(exploit_raw_packets):
  108. eth_frame = Ether(pkt[0])
  109. ip_pkt = eth_frame.payload
  110. tcp_pkt = ip_pkt.payload
  111. if pkt_num == 0:
  112. if tcp_pkt.getfieldval("dport") == self.smb_port:
  113. orig_ip_dst = ip_pkt.getfieldval("dst") # victim IP
  114. # Request
  115. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  116. # Ether
  117. eth_frame.setfieldval("src", mac_source)
  118. eth_frame.setfieldval("dst", mac_destination)
  119. # IP
  120. ip_pkt.setfieldval("src", ip_source)
  121. ip_pkt.setfieldval("dst", ip_destination)
  122. ip_pkt.setfieldval("ttl", source_ttl_value)
  123. # TCP
  124. tcp_pkt.setfieldval("sport",port_source)
  125. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  126. new_pkt.time = timestamp_next_pkt
  127. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  128. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  129. # Reply
  130. else:
  131. # Ether
  132. eth_frame.setfieldval("src", mac_destination)
  133. eth_frame.setfieldval("dst", mac_source)
  134. # IP
  135. ip_pkt.setfieldval("src", ip_destination)
  136. ip_pkt.setfieldval("dst", ip_source)
  137. ip_pkt.setfieldval("ttl", destination_ttl_value)
  138. # TCP
  139. tcp_pkt.setfieldval("dport", port_source)
  140. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  141. timestamp_next_pkt = timestamp_next_pkt + uniform(minDelay, maxDelay)
  142. new_pkt.time = timestamp_next_pkt
  143. packets.append(new_pkt)
  144. # Inject EternalBlue exploit packets
  145. # Read Win7_eternalblue_exploit pcap file
  146. exploit_raw_packets = RawPcapReader("resources/Win7_eternalblue_exploit.pcap")
  147. port_source = randint(self.minDefaultPort,self.maxDefaultPort) # experiments show this range of ports
  148. # conversations = {(ip.src, ip.dst, port.src, port.dst): packets}
  149. conversations, orderList_conversations = self.packetsToConvs(exploit_raw_packets)
  150. conv_start_timesamp = timestamp_next_pkt
  151. for conv_index, conv in enumerate(orderList_conversations):
  152. conv_start_timesamp = conv_start_timesamp + uniform(0.001,0.01) # the distance between the starts of the converstaions
  153. timestamp_next_pkt = conv_start_timesamp
  154. conv_pkts = conversations[conv]
  155. inter_arrival_time_dist = self.get_inter_arrival_time_dist(conv_pkts)
  156. timeSteps = Lea.fromValFreqsDict(inter_arrival_time_dist)
  157. if conv_index == len(orderList_conversations) - 2: # Not the last conversation
  158. timestamp_next_pkt = packets[-1].time + uniform(0.001,0.01)
  159. if conv_index != len(orderList_conversations)-1: # Not the last conversation
  160. port_source += 2
  161. for pkt_num, pkt in enumerate(conv_pkts):
  162. eth_frame = Ether(pkt[0])
  163. ip_pkt = eth_frame.payload
  164. tcp_pkt = ip_pkt.payload
  165. if pkt_num == 0:
  166. if tcp_pkt.getfieldval("dport") == self.smb_port:
  167. orig_ip_dst = ip_pkt.getfieldval("dst")
  168. # defining req/rep should be adapted to fit the last converstaion where
  169. # victim starts a connection with the attacker
  170. # Request
  171. if ip_pkt.getfieldval("dst") == orig_ip_dst: # victim IP
  172. # Ether
  173. eth_frame.setfieldval("src", mac_source)
  174. eth_frame.setfieldval("dst", mac_destination)
  175. # IP
  176. ip_pkt.setfieldval("src", ip_source)
  177. ip_pkt.setfieldval("dst", ip_destination)
  178. ip_pkt.setfieldval("ttl", source_ttl_value)
  179. # TCP
  180. tcp_pkt.setfieldval("sport", port_source)
  181. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  182. new_pkt.time = timestamp_next_pkt
  183. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  184. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + float(timeSteps.random())
  185. # Reply
  186. else:
  187. # Ether
  188. eth_frame.setfieldval("src", mac_destination)
  189. eth_frame.setfieldval("dst", mac_source)
  190. # IP
  191. ip_pkt.setfieldval("src", ip_destination)
  192. ip_pkt.setfieldval("dst", ip_source)
  193. ip_pkt.setfieldval("ttl", destination_ttl_value)
  194. # TCP
  195. tcp_pkt.setfieldval("dport", port_source)
  196. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  197. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  198. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + float(timeSteps.random())
  199. new_pkt.time = timestamp_next_pkt
  200. packets.append(new_pkt)
  201. else: # Last conversation where the victim start a connection with the attacker
  202. timestamp_next_pkt = packets[-1].time + uniform(0.001, 0.01)
  203. port_source = randint(self.minDefaultPort,self.maxDefaultPort)
  204. for pkt_num, pkt in enumerate(conv_pkts):
  205. eth_frame = Ether(pkt[0])
  206. ip_pkt = eth_frame.payload
  207. tcp_pkt = ip_pkt.payload
  208. # defining req/rep should be adapted to fit the last converstaion where
  209. # victim start a connection with the attacker
  210. # Request
  211. if tcp_pkt.getfieldval("dport") == self.last_conn_dst_port:
  212. # Ether
  213. eth_frame.setfieldval("src", mac_destination)
  214. eth_frame.setfieldval("dst", mac_source)
  215. # IP
  216. ip_pkt.setfieldval("src", ip_destination)
  217. ip_pkt.setfieldval("dst", ip_source)
  218. ip_pkt.setfieldval("ttl", destination_ttl_value)
  219. # TCP
  220. tcp_pkt.setfieldval("sport", port_source)
  221. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  222. new_pkt.time = timestamp_next_pkt
  223. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  224. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + float(timeSteps.random())
  225. # Reply
  226. else:
  227. # Ether
  228. eth_frame.setfieldval("src", mac_source)
  229. eth_frame.setfieldval("dst", mac_destination)
  230. # IP
  231. ip_pkt.setfieldval("src", ip_source)
  232. ip_pkt.setfieldval("dst", ip_destination)
  233. ip_pkt.setfieldval("ttl", source_ttl_value)
  234. # TCP
  235. tcp_pkt.setfieldval("dport", port_source)
  236. new_pkt = (eth_frame / ip_pkt / tcp_pkt)
  237. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  238. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps) + float(timeSteps.random())
  239. new_pkt.time = timestamp_next_pkt
  240. packets.append(new_pkt)
  241. # Store timestamp of first packet (for attack label)
  242. self.attack_start_utime = packets[0].time
  243. self.attack_end_utime = packets[-1].time
  244. if len(packets) > 0:
  245. packets = sorted(packets, key=lambda pkt: pkt.time)
  246. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  247. # return packets sorted by packet time_sec_start
  248. # pkt_num+1: because pkt_num starts at 0
  249. return pkt_num + 1, path_attack_pcap