SMBScanAttack.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. from ID2TLib.SMB2 import *
  8. from ID2TLib.Utility import *
  9. from ID2TLib.SMBLib import *
  10. logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
  11. # noinspection PyPep8
  12. from scapy.layers.inet import IP, Ether, TCP
  13. from scapy.layers.smb import *
  14. from scapy.layers.netbios import *
  15. class SMBScanAttack(BaseAttack.BaseAttack):
  16. def __init__(self):
  17. """
  18. Creates a new instance of the SMBScanAttack.
  19. """
  20. # Initialize attack
  21. super(SMBScanAttack, self).__init__("SmbScan Attack", "Injects an SMB scan",
  22. "Scanning/Probing")
  23. # Define allowed parameters and their type
  24. self.supported_params = {
  25. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  26. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  27. Param.PORT_SOURCE: ParameterTypes.TYPE_PORT,
  28. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  29. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  30. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  31. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  32. Param.IP_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  33. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  34. Param.PORT_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  35. Param.HOSTING_IP: ParameterTypes.TYPE_IP_ADDRESS,
  36. Param.HOSTING_VERSION: ParameterTypes.TYPE_STRING,
  37. Param.SOURCE_PLATFORM: ParameterTypes.TYPE_STRING,
  38. Param.PROTOCOL_VERSION: ParameterTypes.TYPE_STRING,
  39. Param.IP_DESTINATION_END: ParameterTypes.TYPE_IP_ADDRESS
  40. }
  41. def init_params(self):
  42. """
  43. Initialize the parameters of this attack using the user supplied command line parameters.
  44. Use the provided statistics to calculate default parameters and to process user
  45. supplied queries.
  46. :param statistics: Reference to a statistics object.
  47. """
  48. # PARAMETERS: initialize with default values
  49. # (values are overwritten if user specifies them)
  50. most_used_ip_address = self.statistics.get_most_used_ip_address()
  51. if isinstance(most_used_ip_address, list):
  52. most_used_ip_address = most_used_ip_address[0]
  53. self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
  54. self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False')
  55. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  56. all_ips = self.statistics.get_ip_addresses()
  57. if not isinstance(all_ips, list):
  58. ip_destinations = []
  59. ip_destinations.append(all_ips)
  60. else:
  61. ip_destinations = all_ips
  62. self.add_param_value(Param.IP_DESTINATION, ip_destinations)
  63. destination_mac = []
  64. for ip in ip_destinations:
  65. destination_mac.append(self.statistics.get_mac_address(str(ip)))
  66. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  67. destination_mac = self.generate_random_mac_address()
  68. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  69. self.add_param_value(Param.PORT_SOURCE, randint(1024, 65535))
  70. self.add_param_value(Param.PORT_SOURCE_RANDOMIZE, 'True')
  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.HOSTING_IP, self.statistics.get_random_ip_address(rnd_ip_count))
  77. self.host_os = get_rnd_os()
  78. self.add_param_value(Param.HOSTING_VERSION, get_smb_version(platform=self.host_os))
  79. self.add_param_value(Param.SOURCE_PLATFORM, get_rnd_os())
  80. self.add_param_value(Param.PROTOCOL_VERSION, "1")
  81. self.add_param_value(Param.IP_DESTINATION_END, "0.0.0.0")
  82. def generate_attack_pcap(self):
  83. def update_timestamp(timestamp, pps, delay=0):
  84. """
  85. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  86. :return: Timestamp to be used for the next packet.
  87. """
  88. if delay == 0:
  89. # Calculate request timestamp
  90. # To imitate the bursty behavior of traffic
  91. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 20, 5 / pps: 7, 10 / pps: 3})
  92. return timestamp + uniform(1/pps , randomdelay.random())
  93. else:
  94. # Calculate reply timestamp
  95. randomdelay = Lea.fromValFreqsDict({2*delay: 70, 3*delay: 20, 5*delay: 7, 10*delay: 3})
  96. return timestamp + uniform(1 / pps + delay, 1 / pps + randomdelay.random())
  97. def getIntervalPPS(complement_interval_pps, timestamp):
  98. """
  99. Gets the packet rate (pps) for a specific time interval.
  100. :param complement_interval_pps: an array of tuples (the last timestamp in the interval, the packet rate in the crresponding interval).
  101. :param timestamp: the timestamp at which the packet rate is required.
  102. :return: the corresponding packet rate (pps) .
  103. """
  104. for row in complement_interval_pps:
  105. if timestamp<=row[0]:
  106. return row[1]
  107. return complement_interval_pps[-1][1] # in case the timstamp > capture max timestamp
  108. def get_ip_data(ip_address: str):
  109. """
  110. Gets the MSS, TTL and Windows Size values of a given IP
  111. :param ip_address: the ip of which (packet-)data shall be returned
  112. :return: MSS, TTL and Window Size values of the given IP
  113. """
  114. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  115. mss_dist = self.statistics.get_mss_distribution(ip_address)
  116. if len(mss_dist) > 0:
  117. mss_prob_dict = Lea.fromValFreqsDict(mss_dist)
  118. mss_value = mss_prob_dict.random()
  119. else:
  120. mss_value = self.statistics.process_db_query("most_used(mssValue)")
  121. # Set TTL based on TTL distribution of IP address
  122. ttl_dist = self.statistics.get_ttl_distribution(ip_address)
  123. if len(ttl_dist) > 0:
  124. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  125. ttl_value = ttl_prob_dict.random()
  126. else:
  127. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  128. # Set Window Size based on Window Size distribution of IP address
  129. win_dist = self.statistics.get_win_distribution(ip_address)
  130. if len(win_dist) > 0:
  131. win_prob_dict = Lea.fromValFreqsDict(win_dist)
  132. win_value = win_prob_dict.random()
  133. else:
  134. win_value = self.statistics.process_db_query("most_used(winSize)")
  135. return mss_value, ttl_value, win_value
  136. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  137. # Calculate complement packet rates of the background traffic for each interval
  138. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  139. # Timestamp
  140. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  141. # store start time of attack
  142. self.attack_start_utime = timestamp_next_pkt
  143. timestamp_prv_reply, timestamp_confirm = 0,0
  144. # Initialize parameters
  145. ip_source = self.get_param_value(Param.IP_SOURCE)
  146. ip_destinations = self.get_param_value(Param.IP_DESTINATION)
  147. hosting_ip = self.get_param_value(Param.HOSTING_IP)
  148. ip_range_end = self.get_param_value(Param.IP_DESTINATION_END)
  149. mac_source = self.get_param_value(Param.MAC_SOURCE)
  150. mac_dest = self.get_param_value(Param.MAC_DESTINATION)
  151. # Check smb version
  152. smb_version = self.get_param_value(Param.PROTOCOL_VERSION)
  153. if smb_version not in smb_versions:
  154. invalid_smb_version(smb_version)
  155. hosting_version = self.get_param_value(Param.HOSTING_VERSION)
  156. if hosting_version not in smb_versions:
  157. invalid_smb_version(hosting_version)
  158. # Check source platform
  159. src_platform = self.get_param_value(Param.SOURCE_PLATFORM).lower()
  160. packets = []
  161. # randomize source ports according to platform, if specified
  162. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  163. sport = generate_source_port_from_platform(src_platform)
  164. else:
  165. sport = self.get_param_value(Param.PORT_SOURCE)
  166. # No destination IP was specified, but a destination MAC was specified, generate IP that fits MAC
  167. if isinstance(ip_destinations, list) and isinstance(mac_dest, str):
  168. ip_destinations = self.statistics.get_ip_address_from_mac(mac_dest)
  169. if len(ip_destinations) == 0:
  170. ip_destinations = self.generate_random_ipv4_address("Unknown", 1)
  171. # Check ip.src == ip.dst
  172. self.ip_src_dst_equal_check(ip_source, ip_destinations)
  173. ip_dests = []
  174. if isinstance(ip_destinations, list):
  175. ip_dests = ip_destinations
  176. else:
  177. ip_dests.append(ip_destinations)
  178. # Generate IPs of destination IP range, if specified
  179. if ip_range_end != "0.0.0.0":
  180. ip_dests = get_ip_range(ip_dests[0], ip_range_end)
  181. shuffle(ip_dests)
  182. # Randomize source IP, if specified
  183. if self.get_param_value(Param.IP_SOURCE_RANDOMIZE):
  184. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  185. while ip_source in ip_dests:
  186. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  187. mac_source = self.statistics.get_mac_address(str(ip_source))
  188. if len(mac_source) == 0:
  189. mac_source = self.generate_random_mac_address()
  190. # Get MSS, TTL and Window size value for source IP
  191. source_mss_value, source_ttl_value, source_win_value = get_ip_data(ip_source)
  192. for ip in ip_dests:
  193. if ip != ip_source:
  194. # Get destination Mac Address
  195. mac_destination = self.statistics.get_mac_address(str(ip))
  196. if len(mac_destination) == 0:
  197. if isinstance(mac_dest, str):
  198. if len(self.statistics.get_ip_address_from_mac(mac_dest)) != 0:
  199. ip = self.statistics.get_ip_address_from_mac(mac_dest)
  200. self.ip_src_dst_equal_check(ip_source, ip)
  201. mac_destination = mac_dest
  202. else:
  203. mac_destination = self.generate_random_mac_address()
  204. # Get MSS, TTL and Window size value for destination IP
  205. destination_mss_value, destination_ttl_value, destination_win_value = get_ip_data(ip)
  206. minDelay, maxDelay = self.get_reply_delay(ip)
  207. # New connection, new random TCP sequence numbers
  208. attacker_seq = randint(1000, 50000)
  209. victim_seq = randint(1000, 50000)
  210. # Randomize source port for each connection if specified
  211. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  212. sport = generate_source_port_from_platform(src_platform, sport)
  213. # 1) Build request package
  214. request_ether = Ether(src=mac_source, dst=mac_destination)
  215. request_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value, flags='DF')
  216. request_tcp = TCP(sport=sport, dport=smb_port, window=source_win_value, flags='S',
  217. seq=attacker_seq, options=[('MSS', source_mss_value)])
  218. attacker_seq += 1
  219. request = (request_ether / request_ip / request_tcp)
  220. request.time = timestamp_next_pkt
  221. # Append request
  222. packets.append(request)
  223. # Update timestamp for next package
  224. timestamp_reply = update_timestamp(timestamp_next_pkt, pps, minDelay)
  225. while (timestamp_reply <= timestamp_prv_reply):
  226. timestamp_reply = update_timestamp(timestamp_prv_reply, pps, minDelay)
  227. timestamp_prv_reply = timestamp_reply
  228. if ip in hosting_ip:
  229. # 2) Build TCP packages for ip that hosts SMB
  230. # destination sends SYN, ACK
  231. reply_ether = Ether(src=mac_destination, dst=mac_source)
  232. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  233. reply_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
  234. window=destination_win_value, options=[('MSS', destination_mss_value)])
  235. victim_seq += 1
  236. reply = (reply_ether / reply_ip / reply_tcp)
  237. reply.time = timestamp_reply
  238. packets.append(reply)
  239. # requester confirms, ACK
  240. confirm_ether = request_ether
  241. confirm_ip = request_ip
  242. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  243. window=source_win_value, flags='A')
  244. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  245. timestamp_confirm = update_timestamp(timestamp_reply, pps, minDelay)
  246. confirm.time = timestamp_confirm
  247. packets.append(confirm)
  248. smb_MID = randint(1, 65535)
  249. smb_PID = randint(1, 65535)
  250. smb_req_tail_arr = []
  251. smb_req_tail_size = 0
  252. # select dialects based on smb version
  253. if smb_version is "1":
  254. smb_req_dialects = smb_dialects[0:6]
  255. else:
  256. smb_req_dialects = smb_dialects
  257. if len(smb_req_dialects) == 0:
  258. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail())
  259. smb_req_tail_size = len(SMBNegociate_Protocol_Request_Tail())
  260. else:
  261. for dia in smb_req_dialects:
  262. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
  263. smb_req_tail_size += len(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
  264. smb_req_head = SMBNegociate_Protocol_Request_Header\
  265. (Flags2=0x2801, PID=smb_PID, MID=smb_MID, ByteCount=smb_req_tail_size)
  266. smb_req_length = len(smb_req_head) + smb_req_tail_size
  267. smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
  268. smb_req_tcp = TCP(sport=sport, dport=smb_port, flags='PA', seq=attacker_seq, ack=victim_seq)
  269. smb_req_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  270. smb_req_ether = Ether(src=mac_source, dst=mac_destination)
  271. attacker_seq += len(smb_req_net_bio) + len(smb_req_head) + smb_req_tail_size
  272. smb_req_combined = (smb_req_ether / smb_req_ip / smb_req_tcp / smb_req_net_bio / smb_req_head)
  273. for i in range(0 , len(smb_req_tail_arr)):
  274. smb_req_combined = smb_req_combined / smb_req_tail_arr[i]
  275. timestamp_smb_req = update_timestamp(timestamp_confirm, pps, minDelay)
  276. smb_req_combined.time = timestamp_smb_req
  277. packets.append(smb_req_combined)
  278. # destination confirms SMB request package
  279. reply_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  280. window=destination_win_value, flags='A')
  281. confirm_smb_req = (reply_ether / reply_ip / reply_tcp)
  282. timestamp_reply = update_timestamp(timestamp_smb_req, pps, minDelay)
  283. confirm_smb_req.time = timestamp_reply
  284. packets.append(confirm_smb_req)
  285. # smb response package
  286. first_timestamp = time.mktime(time.strptime(self.statistics.get_pcap_timestamp_start()[:19],
  287. "%Y-%m-%d %H:%M:%S"))
  288. server_Guid, security_blob, capabilities, data_size, server_start_time = get_smb_platform_data\
  289. (self.host_os, first_timestamp)
  290. timestamp_smb_rsp = update_timestamp(timestamp_reply, pps, minDelay)
  291. diff = timestamp_smb_rsp - timestamp_smb_req
  292. begin = get_filetime_format(timestamp_smb_req+diff*0.1)
  293. end = get_filetime_format(timestamp_smb_rsp-diff*0.1)
  294. system_time = randint(begin, end)
  295. if smb_version is not "1" and hosting_version is not "1":
  296. smb_rsp_paket = SMB2_SYNC_Header(Flags = 1)
  297. smb_rsp_negotiate_body = SMB2_Negotiate_Protocol_Response\
  298. (DialectRevision=0x02ff, SecurityBufferOffset=124, SecurityBufferLength=len(security_blob),
  299. SecurityBlob=security_blob, Capabilities=capabilities, MaxTransactSize=data_size,
  300. MaxReadSize=data_size, MaxWriteSize=data_size, SystemTime=system_time,
  301. ServerStartTime=server_start_time, ServerGuid=server_Guid)
  302. smb_rsp_length = len(smb_rsp_paket) + len(smb_rsp_negotiate_body)
  303. else:
  304. smb_rsp_paket = SMBNegociate_Protocol_Response_Advanced_Security\
  305. (Start="\xffSMB", PID=smb_PID, MID=smb_MID, DialectIndex=5, SecurityBlob=security_blob)
  306. smb_rsp_length = len(smb_rsp_paket)
  307. smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
  308. smb_rsp_tcp = TCP(sport=smb_port, dport=sport, flags='PA', seq=victim_seq, ack=attacker_seq)
  309. smb_rsp_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value)
  310. smb_rsp_ether = Ether(src=mac_destination, dst=mac_source)
  311. victim_seq += len(smb_rsp_net_bio) + len(smb_rsp_paket)
  312. if smb_version is not "1"and hosting_version is not "1":
  313. victim_seq += len(smb_rsp_negotiate_body)
  314. smb_rsp_combined = (smb_rsp_ether / smb_rsp_ip / smb_rsp_tcp / smb_rsp_net_bio / smb_rsp_paket)
  315. if smb_version is not "1"and hosting_version is not "1":
  316. smb_rsp_combined = (smb_rsp_combined / smb_rsp_negotiate_body)
  317. smb_rsp_combined.time = timestamp_smb_rsp
  318. packets.append(smb_rsp_combined)
  319. # source confirms SMB response package
  320. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  321. window=source_win_value, flags='A')
  322. confirm_smb_res = (confirm_ether / confirm_ip / confirm_tcp)
  323. timestamp_confirm = update_timestamp(timestamp_smb_rsp, pps, minDelay)
  324. confirm_smb_res.time = timestamp_confirm
  325. packets.append(confirm_smb_res)
  326. # attacker sends FIN ACK
  327. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  328. window=source_win_value, flags='FA')
  329. source_fin_ack = (confirm_ether / confirm_ip / confirm_tcp)
  330. timestamp_src_fin_ack = update_timestamp(timestamp_confirm, pps, minDelay)
  331. source_fin_ack.time = timestamp_src_fin_ack
  332. attacker_seq += 1
  333. packets.append(source_fin_ack)
  334. # victim sends FIN ACK
  335. reply_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  336. window=destination_win_value, flags='FA')
  337. destination_fin_ack = (reply_ether / reply_ip / reply_tcp)
  338. timestamp_dest_fin_ack = update_timestamp(timestamp_src_fin_ack, pps, minDelay)
  339. victim_seq += 1
  340. destination_fin_ack.time = timestamp_dest_fin_ack
  341. packets.append(destination_fin_ack)
  342. # source sends final ACK
  343. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  344. window=source_win_value, flags='A')
  345. final_ack = (confirm_ether / confirm_ip / confirm_tcp)
  346. timestamp_final_ack = update_timestamp(timestamp_dest_fin_ack, pps, minDelay)
  347. final_ack.time = timestamp_final_ack
  348. packets.append(final_ack)
  349. else:
  350. # Build RST package
  351. reply_ether = Ether(src=mac_destination, dst=mac_source)
  352. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  353. reply_tcp = TCP(sport=smb_port, dport=sport, seq=0, ack=attacker_seq, flags='RA',
  354. window=destination_win_value, options=[('MSS', destination_mss_value)])
  355. reply = (reply_ether / reply_ip / reply_tcp)
  356. reply.time = timestamp_reply
  357. packets.append(reply)
  358. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  359. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  360. # store end time of attack
  361. self.attack_end_utime = packets[-1].time
  362. # write attack packets to pcap
  363. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  364. # return packets sorted by packet time_sec_start
  365. return len(packets), pcap_path