SMBScanAttack.py 19 KB

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