SMBScanAttack.py 19 KB

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