SMBScanAttack.py 21 KB

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