SMBScanAttack.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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 get_ip_data(ip_address: str):
  84. """
  85. Gets the MSS, TTL and Windows Size values of a given IP
  86. :param ip_address: the ip of which (packet-)data shall be returned
  87. :return: MSS, TTL and Window Size values of the given IP
  88. """
  89. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  90. mss_dist = self.statistics.get_mss_distribution(ip_address)
  91. if len(mss_dist) > 0:
  92. mss_prob_dict = Lea.fromValFreqsDict(mss_dist)
  93. mss_value = mss_prob_dict.random()
  94. else:
  95. mss_value = self.statistics.process_db_query("most_used(mssValue)")
  96. # Set TTL based on TTL distribution of IP address
  97. ttl_dist = self.statistics.get_ttl_distribution(ip_address)
  98. if len(ttl_dist) > 0:
  99. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  100. ttl_value = ttl_prob_dict.random()
  101. else:
  102. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  103. # Set Window Size based on Window Size distribution of IP address
  104. win_dist = self.statistics.get_win_distribution(ip_address)
  105. if len(win_dist) > 0:
  106. win_prob_dict = Lea.fromValFreqsDict(win_dist)
  107. win_value = win_prob_dict.random()
  108. else:
  109. win_value = self.statistics.process_db_query("most_used(winSize)")
  110. return mss_value, ttl_value, win_value
  111. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  112. # Calculate complement packet rates of the background traffic for each interval
  113. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  114. # Timestamp
  115. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  116. # store start time of attack
  117. self.attack_start_utime = timestamp_next_pkt
  118. timestamp_prv_reply, timestamp_confirm = 0,0
  119. # Initialize parameters
  120. ip_source = self.get_param_value(Param.IP_SOURCE)
  121. ip_destinations = self.get_param_value(Param.IP_DESTINATION)
  122. hosting_ip = self.get_param_value(Param.HOSTING_IP)
  123. ip_range_end = self.get_param_value(Param.IP_DESTINATION_END)
  124. mac_source = self.get_param_value(Param.MAC_SOURCE)
  125. mac_dest = self.get_param_value(Param.MAC_DESTINATION)
  126. # Check smb version
  127. smb_version = self.get_param_value(Param.PROTOCOL_VERSION)
  128. if smb_version not in smb_versions:
  129. invalid_smb_version(smb_version)
  130. hosting_version = self.get_param_value(Param.HOSTING_VERSION)
  131. if hosting_version not in smb_versions:
  132. invalid_smb_version(hosting_version)
  133. # Check source platform
  134. src_platform = self.get_param_value(Param.SOURCE_PLATFORM).lower()
  135. packets = []
  136. # randomize source ports according to platform, if specified
  137. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  138. sport = generate_source_port_from_platform(src_platform)
  139. else:
  140. sport = self.get_param_value(Param.PORT_SOURCE)
  141. # No destination IP was specified, but a destination MAC was specified, generate IP that fits MAC
  142. if isinstance(ip_destinations, list) and isinstance(mac_dest, str):
  143. ip_destinations = self.statistics.get_ip_address_from_mac(mac_dest)
  144. if len(ip_destinations) == 0:
  145. ip_destinations = self.generate_random_ipv4_address("Unknown", 1)
  146. # Check ip.src == ip.dst
  147. self.ip_src_dst_equal_check(ip_source, ip_destinations)
  148. ip_dests = []
  149. if isinstance(ip_destinations, list):
  150. ip_dests = ip_destinations
  151. else:
  152. ip_dests.append(ip_destinations)
  153. # Generate IPs of destination IP range, if specified
  154. if ip_range_end != "0.0.0.0":
  155. ip_dests = get_ip_range(ip_dests[0], ip_range_end)
  156. shuffle(ip_dests)
  157. # Randomize source IP, if specified
  158. if self.get_param_value(Param.IP_SOURCE_RANDOMIZE):
  159. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  160. while ip_source in ip_dests:
  161. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  162. mac_source = self.statistics.get_mac_address(str(ip_source))
  163. if len(mac_source) == 0:
  164. mac_source = self.generate_random_mac_address()
  165. # Get MSS, TTL and Window size value for source IP
  166. source_mss_value, source_ttl_value, source_win_value = get_ip_data(ip_source)
  167. for ip in ip_dests:
  168. if ip != ip_source:
  169. # Get destination Mac Address
  170. mac_destination = self.statistics.get_mac_address(str(ip))
  171. if len(mac_destination) == 0:
  172. if isinstance(mac_dest, str):
  173. if len(self.statistics.get_ip_address_from_mac(mac_dest)) != 0:
  174. ip = self.statistics.get_ip_address_from_mac(mac_dest)
  175. self.ip_src_dst_equal_check(ip_source, ip)
  176. mac_destination = mac_dest
  177. else:
  178. mac_destination = self.generate_random_mac_address()
  179. # Get MSS, TTL and Window size value for destination IP
  180. destination_mss_value, destination_ttl_value, destination_win_value = get_ip_data(ip)
  181. minDelay, maxDelay = self.get_reply_delay(ip)
  182. # New connection, new random TCP sequence numbers
  183. attacker_seq = randint(1000, 50000)
  184. victim_seq = randint(1000, 50000)
  185. # Randomize source port for each connection if specified
  186. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  187. sport = generate_source_port_from_platform(src_platform, sport)
  188. # 1) Build request package
  189. request_ether = Ether(src=mac_source, dst=mac_destination)
  190. request_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value, flags='DF')
  191. request_tcp = TCP(sport=sport, dport=smb_port, window=source_win_value, flags='S',
  192. seq=attacker_seq, options=[('MSS', source_mss_value)])
  193. attacker_seq += 1
  194. request = (request_ether / request_ip / request_tcp)
  195. request.time = timestamp_next_pkt
  196. # Append request
  197. packets.append(request)
  198. # Update timestamp for next package
  199. timestamp_reply = update_timestamp(timestamp_next_pkt, pps, minDelay)
  200. while (timestamp_reply <= timestamp_prv_reply):
  201. timestamp_reply = update_timestamp(timestamp_prv_reply, pps, minDelay)
  202. timestamp_prv_reply = timestamp_reply
  203. if ip in hosting_ip:
  204. # 2) Build TCP packages for ip that hosts SMB
  205. # destination sends SYN, ACK
  206. reply_ether = Ether(src=mac_destination, dst=mac_source)
  207. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  208. reply_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
  209. window=destination_win_value, options=[('MSS', destination_mss_value)])
  210. victim_seq += 1
  211. reply = (reply_ether / reply_ip / reply_tcp)
  212. reply.time = timestamp_reply
  213. packets.append(reply)
  214. # requester confirms, ACK
  215. confirm_ether = request_ether
  216. confirm_ip = request_ip
  217. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  218. window=source_win_value, flags='A')
  219. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  220. timestamp_confirm = update_timestamp(timestamp_reply, pps, minDelay)
  221. confirm.time = timestamp_confirm
  222. packets.append(confirm)
  223. smb_MID = randint(1, 65535)
  224. smb_PID = randint(1, 65535)
  225. smb_req_tail_arr = []
  226. smb_req_tail_size = 0
  227. # select dialects based on smb version
  228. if smb_version is "1":
  229. smb_req_dialects = smb_dialects[0:6]
  230. else:
  231. smb_req_dialects = smb_dialects
  232. if len(smb_req_dialects) == 0:
  233. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail())
  234. smb_req_tail_size = len(SMBNegociate_Protocol_Request_Tail())
  235. else:
  236. for dia in smb_req_dialects:
  237. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
  238. smb_req_tail_size += len(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
  239. smb_req_head = SMBNegociate_Protocol_Request_Header\
  240. (Flags2=0x2801, PID=smb_PID, MID=smb_MID, ByteCount=smb_req_tail_size)
  241. smb_req_length = len(smb_req_head) + smb_req_tail_size
  242. smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
  243. smb_req_tcp = TCP(sport=sport, dport=smb_port, flags='PA', seq=attacker_seq, ack=victim_seq)
  244. smb_req_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  245. smb_req_ether = Ether(src=mac_source, dst=mac_destination)
  246. attacker_seq += len(smb_req_net_bio) + len(smb_req_head) + smb_req_tail_size
  247. smb_req_combined = (smb_req_ether / smb_req_ip / smb_req_tcp / smb_req_net_bio / smb_req_head)
  248. for i in range(0 , len(smb_req_tail_arr)):
  249. smb_req_combined = smb_req_combined / smb_req_tail_arr[i]
  250. timestamp_smb_req = update_timestamp(timestamp_confirm, pps, minDelay)
  251. smb_req_combined.time = timestamp_smb_req
  252. packets.append(smb_req_combined)
  253. # destination confirms SMB request package
  254. reply_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  255. window=destination_win_value, flags='A')
  256. confirm_smb_req = (reply_ether / reply_ip / reply_tcp)
  257. timestamp_reply = update_timestamp(timestamp_smb_req, pps, minDelay)
  258. confirm_smb_req.time = timestamp_reply
  259. packets.append(confirm_smb_req)
  260. # smb response package
  261. first_timestamp = time.mktime(time.strptime(self.statistics.get_pcap_timestamp_start()[:19],
  262. "%Y-%m-%d %H:%M:%S"))
  263. server_Guid, security_blob, capabilities, data_size, server_start_time = get_smb_platform_data\
  264. (self.host_os, first_timestamp)
  265. timestamp_smb_rsp = update_timestamp(timestamp_reply, pps, minDelay)
  266. diff = timestamp_smb_rsp - timestamp_smb_req
  267. begin = get_filetime_format(timestamp_smb_req+diff*0.1)
  268. end = get_filetime_format(timestamp_smb_rsp-diff*0.1)
  269. system_time = randint(begin, end)
  270. if smb_version is not "1" and hosting_version is not "1":
  271. smb_rsp_paket = SMB2_SYNC_Header(Flags = 1)
  272. smb_rsp_negotiate_body = SMB2_Negotiate_Protocol_Response\
  273. (DialectRevision=0x02ff, SecurityBufferOffset=124, SecurityBufferLength=len(security_blob),
  274. SecurityBlob=security_blob, Capabilities=capabilities, MaxTransactSize=data_size,
  275. MaxReadSize=data_size, MaxWriteSize=data_size, SystemTime=system_time,
  276. ServerStartTime=server_start_time, ServerGuid=server_Guid)
  277. smb_rsp_length = len(smb_rsp_paket) + len(smb_rsp_negotiate_body)
  278. else:
  279. smb_rsp_paket = SMBNegociate_Protocol_Response_Advanced_Security\
  280. (Start="\xffSMB", PID=smb_PID, MID=smb_MID, DialectIndex=5, SecurityBlob=security_blob)
  281. smb_rsp_length = len(smb_rsp_paket)
  282. smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
  283. smb_rsp_tcp = TCP(sport=smb_port, dport=sport, flags='PA', seq=victim_seq, ack=attacker_seq)
  284. smb_rsp_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value)
  285. smb_rsp_ether = Ether(src=mac_destination, dst=mac_source)
  286. victim_seq += len(smb_rsp_net_bio) + len(smb_rsp_paket)
  287. if smb_version is not "1"and hosting_version is not "1":
  288. victim_seq += len(smb_rsp_negotiate_body)
  289. smb_rsp_combined = (smb_rsp_ether / smb_rsp_ip / smb_rsp_tcp / smb_rsp_net_bio / smb_rsp_paket)
  290. if smb_version is not "1"and hosting_version is not "1":
  291. smb_rsp_combined = (smb_rsp_combined / smb_rsp_negotiate_body)
  292. smb_rsp_combined.time = timestamp_smb_rsp
  293. packets.append(smb_rsp_combined)
  294. # source confirms SMB response package
  295. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  296. window=source_win_value, flags='A')
  297. confirm_smb_res = (confirm_ether / confirm_ip / confirm_tcp)
  298. timestamp_confirm = update_timestamp(timestamp_smb_rsp, pps, minDelay)
  299. confirm_smb_res.time = timestamp_confirm
  300. packets.append(confirm_smb_res)
  301. # attacker sends FIN ACK
  302. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  303. window=source_win_value, flags='FA')
  304. source_fin_ack = (confirm_ether / confirm_ip / confirm_tcp)
  305. timestamp_src_fin_ack = update_timestamp(timestamp_confirm, pps, minDelay)
  306. source_fin_ack.time = timestamp_src_fin_ack
  307. attacker_seq += 1
  308. packets.append(source_fin_ack)
  309. # victim sends FIN ACK
  310. reply_tcp = TCP(sport=smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  311. window=destination_win_value, flags='FA')
  312. destination_fin_ack = (reply_ether / reply_ip / reply_tcp)
  313. timestamp_dest_fin_ack = update_timestamp(timestamp_src_fin_ack, pps, minDelay)
  314. victim_seq += 1
  315. destination_fin_ack.time = timestamp_dest_fin_ack
  316. packets.append(destination_fin_ack)
  317. # source sends final ACK
  318. confirm_tcp = TCP(sport=sport, dport=smb_port, seq=attacker_seq, ack=victim_seq,
  319. window=source_win_value, flags='A')
  320. final_ack = (confirm_ether / confirm_ip / confirm_tcp)
  321. timestamp_final_ack = update_timestamp(timestamp_dest_fin_ack, pps, minDelay)
  322. final_ack.time = timestamp_final_ack
  323. packets.append(final_ack)
  324. else:
  325. # Build RST package
  326. reply_ether = Ether(src=mac_destination, dst=mac_source)
  327. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  328. reply_tcp = TCP(sport=smb_port, dport=sport, seq=0, ack=attacker_seq, flags='RA',
  329. window=destination_win_value, options=[('MSS', destination_mss_value)])
  330. reply = (reply_ether / reply_ip / reply_tcp)
  331. reply.time = timestamp_reply
  332. packets.append(reply)
  333. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  334. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  335. # store end time of attack
  336. self.attack_end_utime = packets[-1].time
  337. # write attack packets to pcap
  338. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  339. # return packets sorted by packet time_sec_start
  340. return len(packets), pcap_path