SMBScanAttack.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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. This Attack injects TCP Syn Requests to the port 445 of several ips and related response into the output
  17. pcap file.
  18. If port 445 is open, it will simulate and inject the SMB Protocol Negotiation too.
  19. """
  20. # Initialize attack
  21. super(SMBScanAttack, self).__init__("SmbScan Attack", "Injects an SMB scan",
  22. "Scanning/Probing")
  23. self.host_os = Util.get_rnd_os()
  24. # Define allowed parameters and their type
  25. self.supported_params.update({
  26. atkParam.Parameter.IP_SOURCE: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  27. atkParam.Parameter.IP_DESTINATION: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  28. atkParam.Parameter.PORT_SOURCE: atkParam.ParameterTypes.TYPE_PORT,
  29. atkParam.Parameter.MAC_SOURCE: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  30. atkParam.Parameter.MAC_DESTINATION: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  31. atkParam.Parameter.INJECT_AT_TIMESTAMP: atkParam.ParameterTypes.TYPE_FLOAT,
  32. atkParam.Parameter.INJECT_AFTER_PACKET: atkParam.ParameterTypes.TYPE_PACKET_POSITION,
  33. atkParam.Parameter.IP_SOURCE_RANDOMIZE: atkParam.ParameterTypes.TYPE_BOOLEAN,
  34. atkParam.Parameter.PACKETS_PER_SECOND: atkParam.ParameterTypes.TYPE_FLOAT,
  35. atkParam.Parameter.PORT_SOURCE_RANDOMIZE: atkParam.ParameterTypes.TYPE_BOOLEAN,
  36. atkParam.Parameter.HOSTING_IP: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  37. atkParam.Parameter.HOSTING_VERSION: atkParam.ParameterTypes.TYPE_STRING,
  38. atkParam.Parameter.SOURCE_PLATFORM: atkParam.ParameterTypes.TYPE_STRING,
  39. atkParam.Parameter.PROTOCOL_VERSION: atkParam.ParameterTypes.TYPE_STRING
  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. """
  47. # PARAMETERS: initialize with default values
  48. # (values are overwritten if user specifies them)
  49. most_used_ip_address = self.statistics.get_most_used_ip_address()
  50. self.add_param_value(atkParam.Parameter.IP_SOURCE, most_used_ip_address)
  51. self.add_param_value(atkParam.Parameter.IP_SOURCE_RANDOMIZE, 'False')
  52. self.add_param_value(atkParam.Parameter.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  53. all_ips = self.statistics.get_ip_addresses()
  54. if not isinstance(all_ips, list):
  55. ip_destinations = []
  56. ip_destinations.append(all_ips)
  57. else:
  58. ip_destinations = all_ips
  59. self.add_param_value(atkParam.Parameter.IP_DESTINATION, ip_destinations)
  60. destination_mac = []
  61. for ip in ip_destinations:
  62. destination_mac.append(self.statistics.get_mac_address(str(ip)))
  63. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  64. destination_mac = self.generate_random_mac_address()
  65. self.add_param_value(atkParam.Parameter.MAC_DESTINATION, destination_mac)
  66. self.add_param_value(atkParam.Parameter.PORT_SOURCE, rnd.randint(1024, 65535))
  67. self.add_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE, 'True')
  68. self.add_param_value(atkParam.Parameter.PACKETS_PER_SECOND,
  69. (self.statistics.get_pps_sent(most_used_ip_address) +
  70. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  71. self.add_param_value(atkParam.Parameter.INJECT_AFTER_PACKET, rnd.randint(0, self.statistics.get_packet_count()))
  72. rnd_ip_count = self.statistics.get_ip_address_count() // 2
  73. self.add_param_value(atkParam.Parameter.HOSTING_IP, self.statistics.get_random_ip_address(rnd_ip_count))
  74. self.add_param_value(atkParam.Parameter.HOSTING_VERSION, SMBLib.get_smb_version(platform=self.host_os))
  75. self.add_param_value(atkParam.Parameter.SOURCE_PLATFORM, Util.get_rnd_os())
  76. self.add_param_value(atkParam.Parameter.PROTOCOL_VERSION, "1")
  77. def generate_attack_packets(self):
  78. """
  79. Creates the attack packets.
  80. """
  81. pps = self.get_param_value(atkParam.Parameter.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(atkParam.Parameter.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(atkParam.Parameter.IP_SOURCE)
  91. ip_destinations = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  92. hosting_ip = self.get_param_value(atkParam.Parameter.HOSTING_IP)
  93. mac_source = self.get_param_value(atkParam.Parameter.MAC_SOURCE)
  94. mac_dest = self.get_param_value(atkParam.Parameter.MAC_DESTINATION)
  95. # Check smb version
  96. smb_version = self.get_param_value(atkParam.Parameter.PROTOCOL_VERSION)
  97. if smb_version not in SMBLib.smb_versions:
  98. SMBLib.invalid_smb_version(smb_version)
  99. hosting_version = self.get_param_value(atkParam.Parameter.HOSTING_VERSION)
  100. if hosting_version not in SMBLib.smb_versions:
  101. SMBLib.invalid_smb_version(hosting_version)
  102. # Check source platform
  103. src_platform = self.get_param_value(atkParam.Parameter.SOURCE_PLATFORM).lower()
  104. self.packets = []
  105. # randomize source ports according to platform, if specified
  106. if self.get_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE):
  107. sport = Util.generate_source_port_from_platform(src_platform)
  108. else:
  109. sport = self.get_param_value(atkParam.Parameter.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. rnd.shuffle(ip_dests)
  124. # Randomize source IP, if specified
  125. if self.get_param_value(atkParam.Parameter.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. min_delay, max_delay = self.get_reply_delay(ip)
  149. # New connection, new random TCP sequence numbers
  150. attacker_seq = rnd.randint(1000, 50000)
  151. victim_seq = rnd.randint(1000, 50000)
  152. # Randomize source port for each connection if specified
  153. if self.get_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE):
  154. sport = Util.generate_source_port_from_platform(src_platform, sport)
  155. # 1) Build request package
  156. request_ether = inet.Ether(src=mac_source, dst=mac_destination)
  157. request_ip = inet.IP(src=ip_source, dst=ip, ttl=source_ttl_value, flags='DF')
  158. request_tcp = inet.TCP(sport=sport, dport=SMBLib.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 = Util.update_timestamp(timestamp_next_pkt, pps, min_delay)
  167. while timestamp_reply <= timestamp_prv_reply:
  168. timestamp_reply = Util.update_timestamp(timestamp_prv_reply, pps, min_delay)
  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 = inet.Ether(src=mac_destination, dst=mac_source)
  174. reply_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  175. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  176. flags='SA',
  177. window=destination_win_value, options=[('MSS', destination_mss_value)])
  178. victim_seq += 1
  179. reply = (reply_ether / reply_ip / reply_tcp)
  180. reply.time = timestamp_reply
  181. self.packets.append(reply)
  182. # requester confirms, ACK
  183. confirm_ether = request_ether
  184. confirm_ip = request_ip
  185. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  186. window=source_win_value, flags='A')
  187. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  188. timestamp_confirm = Util.update_timestamp(timestamp_reply, pps, min_delay)
  189. confirm.time = timestamp_confirm
  190. self.packets.append(confirm)
  191. # 3) Build SMB Negotiation packets
  192. smb_mid = rnd.randint(1, 65535)
  193. smb_pid = rnd.randint(1, 65535)
  194. smb_req_tail_arr = []
  195. smb_req_tail_size = 0
  196. # select dialects based on smb version
  197. if smb_version is "1":
  198. smb_req_dialects = SMBLib.smb_dialects[0:6]
  199. else:
  200. smb_req_dialects = SMBLib.smb_dialects
  201. if len(smb_req_dialects) == 0:
  202. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail())
  203. smb_req_tail_size = len(SMBNegociate_Protocol_Request_Tail())
  204. else:
  205. for dia in smb_req_dialects:
  206. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail(BufferData=dia))
  207. smb_req_tail_size += len(SMBNegociate_Protocol_Request_Tail(BufferData=dia))
  208. # Creation of SMB Negotiate Protocol Request packet
  209. smb_req_head = SMBNegociate_Protocol_Request_Header(Flags2=0x2801, PID=smb_pid, MID=smb_mid,
  210. ByteCount=smb_req_tail_size)
  211. smb_req_length = len(smb_req_head) + smb_req_tail_size
  212. smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
  213. smb_req_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, flags='PA', seq=attacker_seq,
  214. ack=victim_seq)
  215. smb_req_ip = inet.IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  216. smb_req_ether = inet.Ether(src=mac_source, dst=mac_destination)
  217. attacker_seq += len(smb_req_net_bio) + len(smb_req_head) + smb_req_tail_size
  218. smb_req_combined = (smb_req_ether / smb_req_ip / smb_req_tcp / smb_req_net_bio / smb_req_head)
  219. for i in range(0, len(smb_req_tail_arr)):
  220. smb_req_combined = smb_req_combined / smb_req_tail_arr[i]
  221. timestamp_smb_req = Util.update_timestamp(timestamp_confirm, pps, min_delay)
  222. smb_req_combined.time = timestamp_smb_req
  223. self.packets.append(smb_req_combined)
  224. # destination confirms SMB request package
  225. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  226. window=destination_win_value, flags='A')
  227. confirm_smb_req = (reply_ether / reply_ip / reply_tcp)
  228. timestamp_reply = Util.update_timestamp(timestamp_smb_req, pps, min_delay)
  229. confirm_smb_req.time = timestamp_reply
  230. self.packets.append(confirm_smb_req)
  231. # smb response package
  232. first_timestamp = time.mktime(time.strptime(self.statistics.get_pcap_timestamp_start()[:19],
  233. "%Y-%m-%d %H:%M:%S"))
  234. server_guid, security_blob, capabilities, data_size, server_start_time =\
  235. SMBLib.get_smb_platform_data(self.host_os, first_timestamp)
  236. timestamp_smb_rsp = Util.update_timestamp(timestamp_reply, pps, min_delay)
  237. diff = timestamp_smb_rsp - timestamp_smb_req
  238. begin = Util.get_filetime_format(timestamp_smb_req + diff * 0.1)
  239. end = Util.get_filetime_format(timestamp_smb_rsp - diff * 0.1)
  240. system_time = rnd.randint(begin, end)
  241. # Creation of SMB Negotiate Protocol Response packets
  242. if smb_version is not "1" and hosting_version is not "1":
  243. smb_rsp_packet = SMB2.SMB2_SYNC_Header(Flags=1)
  244. smb_rsp_negotiate_body =\
  245. SMB2.SMB2_Negotiate_Protocol_Response(DialectRevision=0x02ff, SecurityBufferOffset=124,
  246. SecurityBufferLength=len(security_blob),
  247. SecurityBlob=security_blob, Capabilities=capabilities,
  248. MaxTransactSize=data_size, MaxReadSize=data_size,
  249. MaxWriteSize=data_size, SystemTime=system_time,
  250. ServerStartTime=server_start_time,
  251. ServerGuid=server_guid)
  252. smb_rsp_length = len(smb_rsp_packet) + len(smb_rsp_negotiate_body)
  253. else:
  254. smb_rsp_packet =\
  255. SMBNegociate_Protocol_Response_Advanced_Security(Start="\xffSMB", PID=smb_pid, MID=smb_mid,
  256. DialectIndex=5, SecurityBlob=security_blob)
  257. smb_rsp_length = len(smb_rsp_packet)
  258. smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
  259. smb_rsp_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, flags='PA', seq=victim_seq,
  260. ack=attacker_seq)
  261. smb_rsp_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value)
  262. smb_rsp_ether = inet.Ether(src=mac_destination, dst=mac_source)
  263. victim_seq += len(smb_rsp_net_bio) + len(smb_rsp_packet)
  264. if smb_version is not "1" and hosting_version is not "1":
  265. victim_seq += len(smb_rsp_negotiate_body)
  266. smb_rsp_combined = (smb_rsp_ether / smb_rsp_ip / smb_rsp_tcp / smb_rsp_net_bio / smb_rsp_packet)
  267. if smb_version is not "1" and hosting_version is not "1":
  268. smb_rsp_combined = (smb_rsp_combined / smb_rsp_negotiate_body)
  269. smb_rsp_combined.time = timestamp_smb_rsp
  270. self.packets.append(smb_rsp_combined)
  271. # source confirms SMB response package
  272. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  273. window=source_win_value, flags='A')
  274. confirm_smb_res = (confirm_ether / confirm_ip / confirm_tcp)
  275. timestamp_confirm = Util.update_timestamp(timestamp_smb_rsp, pps, min_delay)
  276. confirm_smb_res.time = timestamp_confirm
  277. self.packets.append(confirm_smb_res)
  278. # attacker sends FIN ACK
  279. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  280. window=source_win_value, flags='FA')
  281. source_fin_ack = (confirm_ether / confirm_ip / confirm_tcp)
  282. timestamp_src_fin_ack = Util.update_timestamp(timestamp_confirm, pps, min_delay)
  283. source_fin_ack.time = timestamp_src_fin_ack
  284. attacker_seq += 1
  285. self.packets.append(source_fin_ack)
  286. # victim sends FIN ACK
  287. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  288. window=destination_win_value, flags='FA')
  289. destination_fin_ack = (reply_ether / reply_ip / reply_tcp)
  290. timestamp_dest_fin_ack = Util.update_timestamp(timestamp_src_fin_ack, pps, min_delay)
  291. victim_seq += 1
  292. destination_fin_ack.time = timestamp_dest_fin_ack
  293. self.packets.append(destination_fin_ack)
  294. # source sends final ACK
  295. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  296. window=source_win_value, flags='A')
  297. final_ack = (confirm_ether / confirm_ip / confirm_tcp)
  298. timestamp_final_ack = Util.update_timestamp(timestamp_dest_fin_ack, pps, min_delay)
  299. final_ack.time = timestamp_final_ack
  300. self.packets.append(final_ack)
  301. else:
  302. # Build RST package
  303. reply_ether = inet.Ether(src=mac_destination, dst=mac_source)
  304. reply_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  305. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=0, ack=attacker_seq, flags='RA',
  306. window=destination_win_value, options=[('MSS', destination_mss_value)])
  307. reply = (reply_ether / reply_ip / reply_tcp)
  308. reply.time = timestamp_reply
  309. self.packets.append(reply)
  310. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  311. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps)
  312. def generate_attack_pcap(self):
  313. """
  314. Creates a pcap containing the attack packets.
  315. :return: The location of the generated pcap file.
  316. """
  317. # store end time of attack
  318. self.attack_end_utime = self.packets[-1].time
  319. # write attack self.packets to pcap
  320. pcap_path = self.write_attack_pcap(sorted(self.packets, key=lambda pkt: pkt.time))
  321. # return packets sorted by packet time_sec_start
  322. return len(self.packets), pcap_path