SmbScanAttack.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. import logging
  2. from random import shuffle, randint, choice, 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.layers.inet import IP, Ether, TCP
  10. from scapy.layers.smb import *
  11. from scapy.layers.netbios import *
  12. class SmbScanAttack(BaseAttack.BaseAttack):
  13. # SMB port
  14. smb_port = 445
  15. def __init__(self):
  16. """
  17. Creates a new instance of the SmbScanAttack.
  18. """
  19. # Initialize attack
  20. super(SmbScanAttack, self).__init__("SmbScan Attack", "Injects an SMB scan",
  21. "Scanning/Probing")
  22. # Define allowed parameters and their type
  23. self.supported_params = {
  24. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  25. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  26. Param.PORT_SOURCE: ParameterTypes.TYPE_PORT,
  27. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  28. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  29. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  30. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  31. Param.IP_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  32. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  33. Param.PORT_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  34. Param.IP_HOSTING: ParameterTypes.TYPE_IP_ADDRESS,
  35. Param.PROTOCOL_VERSION: ParameterTypes.TYPE_STRING
  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 values
  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.IP_SOURCE_RANDOMIZE, 'False')
  51. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  52. all_ips = self.statistics.get_ip_addresses()
  53. if not isinstance(all_ips, list):
  54. ip_destinations = []
  55. ip_destinations.append(all_ips)
  56. else:
  57. ip_destinations = all_ips
  58. self.add_param_value(Param.IP_DESTINATION, ip_destinations)
  59. # MAYBE REMOVE/CHANGE THIS MAC STUFF
  60. #
  61. destination_mac = []
  62. for ip in ip_destinations:
  63. destination_mac.append(self.statistics.get_mac_address(str(ip)))
  64. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  65. destination_mac = self.generate_random_mac_address()
  66. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  67. #
  68. #
  69. self.add_param_value(Param.PORT_SOURCE, randint(1024, 65535))
  70. self.add_param_value(Param.PORT_SOURCE_RANDOMIZE, 'False')
  71. self.add_param_value(Param.PACKETS_PER_SECOND,
  72. (self.statistics.get_pps_sent(most_used_ip_address) +
  73. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  74. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  75. rnd_ip_count = self.statistics.get_ip_address_count()/2
  76. self.add_param_value(Param.IP_HOSTING, self.statistics.get_random_ip_address(rnd_ip_count))
  77. # maybe change to version 1 as default
  78. self.add_param_value(Param.PROTOCOL_VERSION, "2.1")
  79. @property
  80. def generate_attack_pcap(self):
  81. def update_timestamp(timestamp, pps, delay=0):
  82. """
  83. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  84. :return: Timestamp to be used for the next packet.
  85. """
  86. if delay == 0:
  87. # Calculate request timestamp
  88. # To imitate the bursty behavior of traffic
  89. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 20, 5 / pps: 7, 10 / pps: 3})
  90. return timestamp + uniform(1/pps , randomdelay.random())
  91. else:
  92. # Calculate reply timestamp
  93. randomdelay = Lea.fromValFreqsDict({2*delay: 70, 3*delay: 20, 5*delay: 7, 10*delay: 3})
  94. return timestamp + uniform(1 / pps + delay, 1 / pps + randomdelay.random())
  95. def getIntervalPPS(complement_interval_pps, timestamp):
  96. """
  97. Gets the packet rate (pps) for a specific time interval.
  98. :param complement_interval_pps: an array of tuples (the last timestamp in the interval, the packet rate in the crresponding interval).
  99. :param timestamp: the timestamp at which the packet rate is required.
  100. :return: the corresponding packet rate (pps) .
  101. """
  102. for row in complement_interval_pps:
  103. if timestamp<=row[0]:
  104. return row[1]
  105. return complement_interval_pps[-1][1] # in case the timstamp > capture max timestamp
  106. mac_source = self.get_param_value(Param.MAC_SOURCE)
  107. mac_dest = self.get_param_value(Param.MAC_DESTINATION)
  108. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  109. # Calculate complement packet rates of the background traffic for each interval
  110. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  111. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  112. sport = randint(1, 65535)
  113. else:
  114. sport = self.get_param_value(Param.PORT_SOURCE)
  115. # Timestamp
  116. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  117. # store start time of attack
  118. self.attack_start_utime = timestamp_next_pkt
  119. timestamp_prv_reply, timestamp_confirm = 0,0
  120. # Initialize parameters
  121. ip_source = self.get_param_value(Param.IP_SOURCE)
  122. ip_destinations = self.get_param_value(Param.IP_DESTINATION)
  123. ip_hosting = self.get_param_value(Param.IP_HOSTING)
  124. packets = []
  125. # Check ip.src == ip.dst
  126. #self.ip_src_dst_equal_check(ip_source, ip_destination)
  127. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  128. source_mss_dist = self.statistics.get_mss_distribution(ip_source)
  129. if len(source_mss_dist) > 0:
  130. source_mss_prob_dict = Lea.fromValFreqsDict(source_mss_dist)
  131. source_mss_value = source_mss_prob_dict.random()
  132. else:
  133. source_mss_value = self.statistics.process_db_query("most_used(mssValue)")
  134. # Set TTL based on TTL distribution of IP address
  135. source_ttl_dist = self.statistics.get_ttl_distribution(ip_source)
  136. if len(source_ttl_dist) > 0:
  137. source_ttl_prob_dict = Lea.fromValFreqsDict(source_ttl_dist)
  138. source_ttl_value = source_ttl_prob_dict.random()
  139. else:
  140. source_ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  141. # Set Window Size based on Window Size distribution of IP address
  142. source_win_dist = self.statistics.get_win_distribution(ip_source)
  143. if len(source_win_dist) > 0:
  144. source_win_prob_dict = Lea.fromValFreqsDict(source_win_dist)
  145. source_win_value = source_win_prob_dict.random()
  146. else:
  147. source_win_value = self.statistics.process_db_query("most_used(winSize)")
  148. # ACTUAL ATTACK GOES HERE
  149. #print(len(mac_dest))
  150. #print(mac_destination)
  151. #print(len(ip_destinations))
  152. #print(ip_destinations)
  153. #print(ip_source)
  154. ip_dests = []
  155. if isinstance(ip_destinations, list):
  156. ip_dests = ip_destinations
  157. else:
  158. ip_dests.append(ip_destinations)
  159. #print(ip_dests)
  160. for ip in ip_dests:
  161. if ip != ip_source:
  162. # Get destination Mac Address
  163. #print(ip)
  164. mac_destination = self.statistics.get_mac_address(str(ip))
  165. if len(mac_destination) == 0:
  166. if isinstance(mac_dest, str):
  167. mac_destination = mac_dest
  168. else:
  169. mac_destination = self.generate_random_mac_address()
  170. #print(len(mac_destination))
  171. #print(mac_destination)
  172. #print(ip)
  173. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  174. destination_mss_dist = self.statistics.get_mss_distribution(ip)
  175. if len(destination_mss_dist) > 0:
  176. destination_mss_prob_dict = Lea.fromValFreqsDict(destination_mss_dist)
  177. destination_mss_value = destination_mss_prob_dict.random()
  178. else:
  179. destination_mss_value = self.statistics.process_db_query("most_used(mssValue)")
  180. # Set TTL based on TTL distribution of IP address
  181. destination_ttl_dist = self.statistics.get_ttl_distribution(ip)
  182. if len(destination_ttl_dist) > 0:
  183. destination_ttl_prob_dict = Lea.fromValFreqsDict(destination_ttl_dist)
  184. destination_ttl_value = destination_ttl_prob_dict.random()
  185. else:
  186. destination_ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  187. # Set Window Size based on Window Size distribution of IP address
  188. destination_win_dist = self.statistics.get_win_distribution(ip)
  189. if len(destination_win_dist) > 0:
  190. destination_win_prob_dict = Lea.fromValFreqsDict(destination_win_dist)
  191. destination_win_value = destination_win_prob_dict.random()
  192. else:
  193. destination_win_value = self.statistics.process_db_query("most_used(winSize)")
  194. minDelay, maxDelay = self.get_reply_delay(ip)
  195. # New connection, new random TCP sequence numbers
  196. attacker_seq = randint(1000, 50000)
  197. victim_seq = randint(1000, 50000)
  198. # 1) Build request package
  199. request_ether = Ether(src=mac_source, dst=mac_destination)
  200. request_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value, flags='DF')
  201. request_tcp = TCP(sport=sport, dport=self.smb_port, window=source_win_value, flags='S',
  202. seq=attacker_seq, options=[('MSS', source_mss_value)])
  203. attacker_seq += 1
  204. request = (request_ether / request_ip / request_tcp)
  205. request.time = timestamp_next_pkt
  206. # Append request
  207. packets.append(request)
  208. # Update timestamp for next package
  209. timestamp_reply = update_timestamp(timestamp_next_pkt, pps, minDelay)
  210. while (timestamp_reply <= timestamp_prv_reply):
  211. timestamp_reply = update_timestamp(timestamp_prv_reply, pps, minDelay)
  212. timestamp_prv_reply = timestamp_reply
  213. if ip in ip_hosting:
  214. # 2) Build TCP packages for ip that hosts SMB
  215. # destination sends SYN, ACK
  216. reply_ether = Ether(src=mac_destination, dst=mac_source)
  217. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  218. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA', window=destination_win_value,
  219. options=[('MSS', destination_mss_value)])
  220. victim_seq += 1
  221. reply = (reply_ether / reply_ip / reply_tcp)
  222. reply.time = timestamp_reply
  223. packets.append(reply)
  224. # requester confirms, ACK
  225. confirm_ether = request_ether
  226. confirm_ip = request_ip
  227. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq, window=source_win_value, flags='A')
  228. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  229. timestamp_confirm = update_timestamp(timestamp_reply, pps, minDelay)
  230. confirm.time = timestamp_confirm
  231. packets.append(confirm)
  232. # INSERT SMB-REQUEST PACKAGE HERE
  233. # CHECK FOR PROTOCOL VERSION?
  234. smb_MID = randint(1, 65535)
  235. smb_PID = randint(1, 65535)
  236. smb_req_tail = SMBNegociate_Protocol_Request_Tail()
  237. smb_req_head = SMBNegociate_Protocol_Request_Header(Flags2=0x2801, PID=smb_PID, MID=smb_MID)
  238. smb_req_length = len(smb_req_head) + len(smb_req_tail)
  239. smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
  240. smb_req_tcp = TCP(sport=sport, dport=self.smb_port, flags='PA', seq=attacker_seq, ack=victim_seq)
  241. smb_req_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  242. smb_req_ether = Ether(src=mac_source, dst=mac_destination)
  243. attacker_seq += len(smb_req_net_bio) + len(smb_req_head) + len(smb_req_tail)
  244. smb_req_combined = (
  245. smb_req_ether / smb_req_ip / smb_req_tcp / smb_req_net_bio / smb_req_head / smb_req_tail)
  246. timestamp_smb_req = update_timestamp(timestamp_confirm, pps, minDelay)
  247. smb_req_combined.time = timestamp_smb_req
  248. packets.append(smb_req_combined)
  249. # destination confirms SMB request package
  250. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, window=destination_win_value, flags='A')
  251. confirm_smb_req = (reply_ether / reply_ip / reply_tcp)
  252. timestamp_reply = update_timestamp(timestamp_smb_req, pps, minDelay)
  253. confirm_smb_req.time = timestamp_reply
  254. packets.append(confirm_smb_req)
  255. # INSERT SMB-RESPONSE PACKAGE HERE
  256. # CHECK FOR PROTOCOL VERSION?
  257. smb_rsp_paket = SMBNegociate_Protocol_Response_No_Security_No_Key()
  258. smb_rsp_length = len(smb_rsp_paket)
  259. smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
  260. smb_rsp_tcp = TCP(sport=self.smb_port, dport=sport, flags='PA', seq=victim_seq, ack=attacker_seq)
  261. smb_rsp_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value)
  262. smb_rsp_ether = Ether(src=mac_destination, dst=mac_source)
  263. victim_seq += len(smb_rsp_net_bio) + len(smb_rsp_paket)
  264. smb_rsp_combined = (smb_rsp_ether / smb_rsp_ip / smb_rsp_tcp / smb_rsp_net_bio / smb_rsp_paket)
  265. timestamp_smb_rsp = update_timestamp(timestamp_reply, pps, minDelay)
  266. smb_rsp_combined.time = timestamp_smb_rsp
  267. packets.append(smb_rsp_combined)
  268. # source confirms SMB response package
  269. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq, window=source_win_value, flags='A')
  270. confirm_smb_res = (confirm_ether / confirm_ip / confirm_tcp)
  271. timestamp_confirm = update_timestamp(timestamp_smb_rsp, pps, minDelay)
  272. confirm_smb_res.time = timestamp_confirm
  273. packets.append(confirm_smb_res)
  274. # attacker sends FIN ACK
  275. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq, window=source_win_value, flags='FA')
  276. source_fin_ack = (confirm_ether / confirm_ip / confirm_tcp)
  277. timestamp_src_fin_ack = update_timestamp(timestamp_confirm, pps, minDelay)
  278. source_fin_ack.time = timestamp_src_fin_ack
  279. attacker_seq += 1
  280. packets.append(source_fin_ack)
  281. # victim sends FIN ACK
  282. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, window=destination_win_value, flags='FA')
  283. destination_fin_ack = (reply_ether / reply_ip / reply_tcp)
  284. timestamp_dest_fin_ack = update_timestamp(timestamp_src_fin_ack, pps, minDelay)
  285. victim_seq += 1
  286. destination_fin_ack.time = timestamp_dest_fin_ack
  287. packets.append(destination_fin_ack)
  288. # source sends final ACK
  289. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq, window=source_win_value, flags='A')
  290. final_ack = (confirm_ether / confirm_ip / confirm_tcp)
  291. timestamp_final_ack = update_timestamp(timestamp_dest_fin_ack, pps, minDelay)
  292. final_ack.time = timestamp_final_ack
  293. packets.append(final_ack)
  294. else:
  295. # Build RST package
  296. reply_ether = Ether(src=mac_destination, dst=mac_source)
  297. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  298. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=0, ack=attacker_seq, flags='RA', window=destination_win_value,
  299. options=[('MSS', destination_mss_value)])
  300. reply = (reply_ether / reply_ip / reply_tcp)
  301. reply.time = timestamp_reply
  302. packets.append(reply)
  303. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  304. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  305. # store end time of attack
  306. self.attack_end_utime = packets[-1].time
  307. # write attack packets to pcap
  308. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  309. # return packets sorted by packet time_sec_start
  310. return len(packets), pcap_path