SmbScanAttack.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. import logging
  2. from random import shuffle, randint, choice, uniform
  3. from datetime import datetime, timedelta, tzinfo
  4. from calendar import timegm
  5. from lea import Lea
  6. from Attack import BaseAttack
  7. from Attack.AttackParameters import Parameter as Param
  8. from Attack.AttackParameters import ParameterTypes
  9. from ID2TLib.smb2 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. platforms = {"win7", "win10", "winxp", "win8.1", "macos", "linux", "win8", "winvista", "winnt", "win2000"}
  17. # SMB port
  18. smb_port = 445
  19. # SMB versions
  20. smb_versions = {"1", "2.0", "2.1", "3.0", "3.0.2", "3.1.1"}
  21. smb_versions_per_win = {'win7': "2.1", 'win10': "3.1.1", 'winxp': "1", 'win8.1': "3.0.2", 'win8': "3.0",
  22. 'winvista': "2.0", 'winnt': "1", "win2000": "1"}
  23. smb_versions_per_samba = {'3.6': "2.0", '4.0': "2.1", '4.1': "3.0", '4.3': "3.1.1"}
  24. # SMB dialects
  25. smb_dialects = ["PC NETWORK PROGRAM 1.0", "LANMAN1.0", "Windows for Workgroups 3.1a", "LM1.2X002", "LANMAN2.1",
  26. "NT LM 0.12", "SMB 2.002", "SMB 2.???"]
  27. # SMB security blobs
  28. security_blob_windows = "\x60\x82\x01\x3c\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x82\x01\x30" \
  29. "\x30\x82\x01\x2c\xa0\x1a\x30\x18\x06\x0a\x2b\x06\x01\x04\x01\x82" \
  30. "\x37\x02\x02\x1e\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" \
  31. "\xa2\x82\x01\x0c\x04\x82\x01\x08\x4e\x45\x47\x4f\x45\x58\x54\x53" \
  32. "\x01\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x70\x00\x00\x00" \
  33. "\xbc\x84\x03\x97\x6f\x80\x3b\x81\xa6\x45\x1b\x05\x92\x39\xde\x3d" \
  34. "\xd6\x91\x85\x49\x8a\xd0\x3b\x58\x87\x99\xb4\x98\xdf\xa6\x1d\x73" \
  35. "\x3b\x57\xbf\x05\x63\x5e\x30\xea\xa8\xd8\xd8\x45\xba\x80\x52\xa5" \
  36. "\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00\x00\x00\x01\x00\x00\x00" \
  37. "\x00\x00\x00\x00\x00\x00\x00\x00\x5c\x33\x53\x0d\xea\xf9\x0d\x4d" \
  38. "\xb2\xec\x4a\xe3\x78\x6e\xc3\x08\x4e\x45\x47\x4f\x45\x58\x54\x53" \
  39. "\x03\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\x98\x00\x00\x00" \
  40. "\xbc\x84\x03\x97\x6f\x80\x3b\x81\xa6\x45\x1b\x05\x92\x39\xde\x3d" \
  41. "\x5c\x33\x53\x0d\xea\xf9\x0d\x4d\xb2\xec\x4a\xe3\x78\x6e\xc3\x08" \
  42. "\x40\x00\x00\x00\x58\x00\x00\x00\x30\x56\xa0\x54\x30\x52\x30\x27" \
  43. "\x80\x25\x30\x23\x31\x21\x30\x1f\x06\x03\x55\x04\x03\x13\x18\x54" \
  44. "\x6f\x6b\x65\x6e\x20\x53\x69\x67\x6e\x69\x6e\x67\x20\x50\x75\x62" \
  45. "\x6c\x69\x63\x20\x4b\x65\x79\x30\x27\x80\x25\x30\x23\x31\x21\x30" \
  46. "\x1f\x06\x03\x55\x04\x03\x13\x18\x54\x6f\x6b\x65\x6e\x20\x53\x69" \
  47. "\x67\x6e\x69\x6e\x67\x20\x50\x75\x62\x6c\x69\x63\x20\x4b\x65\x79"
  48. security_blob_ubuntu = "\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e" \
  49. "\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa3\x2a" \
  50. "\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f\x64\x65\x66\x69\x6e\x65" \
  51. "\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31\x37\x38\x40\x70\x6c\x65" \
  52. "\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65"
  53. security_blob_macos = "\x60\x7e\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x74\x30\x72\xa0\x44" \
  54. "\x30\x42\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02\x06\x09\x2a" \
  55. "\x86\x48\x86\xf7\x12\x01\x02\x02\x06\x06\x2a\x85\x70\x2b\x0e\x03" \
  56. "\x06\x06\x2b\x06\x01\x05\x05\x0e\x06\x0a\x2b\x06\x01\x04\x01\x82" \
  57. "\x37\x02\x02\x0a\x06\x06\x2b\x05\x01\x05\x02\x07\x06\x06\x2b\x06" \
  58. "\x01\x05\x02\x05\xa3\x2a\x30\x28\xa0\x26\x1b\x24\x6e\x6f\x74\x5f" \
  59. "\x64\x65\x66\x69\x6e\x65\x64\x5f\x69\x6e\x5f\x52\x46\x43\x34\x31" \
  60. "\x37\x38\x40\x70\x6c\x65\x61\x73\x65\x5f\x69\x67\x6e\x6f\x72\x65"
  61. def __init__(self):
  62. """
  63. Creates a new instance of the SmbScanAttack.
  64. """
  65. # Initialize attack
  66. super(SmbScanAttack, self).__init__("SmbScan Attack", "Injects an SMB scan",
  67. "Scanning/Probing")
  68. # Define allowed parameters and their type
  69. self.supported_params = {
  70. Param.IP_SOURCE: ParameterTypes.TYPE_IP_ADDRESS,
  71. Param.IP_DESTINATION: ParameterTypes.TYPE_IP_ADDRESS,
  72. Param.PORT_SOURCE: ParameterTypes.TYPE_PORT,
  73. Param.MAC_SOURCE: ParameterTypes.TYPE_MAC_ADDRESS,
  74. Param.MAC_DESTINATION: ParameterTypes.TYPE_MAC_ADDRESS,
  75. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  76. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  77. Param.IP_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  78. Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
  79. Param.PORT_SOURCE_RANDOMIZE: ParameterTypes.TYPE_BOOLEAN,
  80. Param.HOSTING_IP: ParameterTypes.TYPE_IP_ADDRESS,
  81. Param.HOSTING_VERSION: ParameterTypes.TYPE_STRING,
  82. Param.SOURCE_PLATFORM: ParameterTypes.TYPE_STRING,
  83. Param.PROTOCOL_VERSION: ParameterTypes.TYPE_STRING,
  84. Param.IP_DESTINATION_END: ParameterTypes.TYPE_IP_ADDRESS
  85. }
  86. def init_params(self):
  87. """
  88. Initialize the parameters of this attack using the user supplied command line parameters.
  89. Use the provided statistics to calculate default parameters and to process user
  90. supplied queries.
  91. :param statistics: Reference to a statistics object.
  92. """
  93. # PARAMETERS: initialize with default values
  94. # (values are overwritten if user specifies them)
  95. most_used_ip_address = self.statistics.get_most_used_ip_address()
  96. if isinstance(most_used_ip_address, list):
  97. most_used_ip_address = most_used_ip_address[0]
  98. self.add_param_value(Param.IP_SOURCE, most_used_ip_address)
  99. self.add_param_value(Param.IP_SOURCE_RANDOMIZE, 'False')
  100. self.add_param_value(Param.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  101. all_ips = self.statistics.get_ip_addresses()
  102. if not isinstance(all_ips, list):
  103. ip_destinations = []
  104. ip_destinations.append(all_ips)
  105. else:
  106. ip_destinations = all_ips
  107. self.add_param_value(Param.IP_DESTINATION, ip_destinations)
  108. # FIXME: MAYBE REMOVE/CHANGE THIS MAC STUFF
  109. destination_mac = []
  110. for ip in ip_destinations:
  111. destination_mac.append(self.statistics.get_mac_address(str(ip)))
  112. if isinstance(destination_mac, list) and len(destination_mac) == 0:
  113. destination_mac = self.generate_random_mac_address()
  114. self.add_param_value(Param.MAC_DESTINATION, destination_mac)
  115. self.add_param_value(Param.PORT_SOURCE, randint(1024, 65535))
  116. self.add_param_value(Param.PORT_SOURCE_RANDOMIZE, 'True')
  117. self.add_param_value(Param.PACKETS_PER_SECOND,
  118. (self.statistics.get_pps_sent(most_used_ip_address) +
  119. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  120. self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
  121. rnd_ip_count = self.statistics.get_ip_address_count()/2
  122. self.add_param_value(Param.HOSTING_IP, self.statistics.get_random_ip_address(rnd_ip_count))
  123. self.host_os = self.get_rnd_os()
  124. self.add_param_value(Param.HOSTING_VERSION, self.get_smb_version(self.host_os))
  125. self.add_param_value(Param.SOURCE_PLATFORM, self.get_rnd_os())
  126. self.add_param_value(Param.PROTOCOL_VERSION, "1")
  127. self.add_param_value(Param.IP_DESTINATION_END, "0.0.0.0")
  128. def get_rnd_os(self):
  129. os_dist = Lea.fromValFreqsDict({"win7": 48.43, "win10": 27.99, "winxp": 6.07, "win8.1": 6.07, "macos": 5.94,
  130. "linux": 3.38, "win8": 1.35, "winvista": 0.46, "winnt": 0.31})
  131. return os_dist.random()
  132. def get_smb_version(self, os: str):
  133. if os is "linux":
  134. return random.choice(list(self.smb_versions_per_samba.values()))
  135. elif os is "macos":
  136. # TODO: figure out macOS smb version(s)
  137. return random.choice(list(self.smb_versions))
  138. else:
  139. return self.smb_versions_per_win[os]
  140. def get_rnd_smb_version(self):
  141. os = self.get_rnd_os()
  142. return self.get_smb_version(os)
  143. def generate_attack_pcap(self):
  144. def update_timestamp(timestamp, pps, delay=0):
  145. """
  146. Calculates the next timestamp to be used based on the packet per second rate (pps) and the maximum delay.
  147. :return: Timestamp to be used for the next packet.
  148. """
  149. if delay == 0:
  150. # Calculate request timestamp
  151. # To imitate the bursty behavior of traffic
  152. randomdelay = Lea.fromValFreqsDict({1 / pps: 70, 2 / pps: 20, 5 / pps: 7, 10 / pps: 3})
  153. return timestamp + uniform(1/pps , randomdelay.random())
  154. else:
  155. # Calculate reply timestamp
  156. randomdelay = Lea.fromValFreqsDict({2*delay: 70, 3*delay: 20, 5*delay: 7, 10*delay: 3})
  157. return timestamp + uniform(1 / pps + delay, 1 / pps + randomdelay.random())
  158. def getIntervalPPS(complement_interval_pps, timestamp):
  159. """
  160. Gets the packet rate (pps) for a specific time interval.
  161. :param complement_interval_pps: an array of tuples (the last timestamp in the interval, the packet rate in the crresponding interval).
  162. :param timestamp: the timestamp at which the packet rate is required.
  163. :return: the corresponding packet rate (pps) .
  164. """
  165. for row in complement_interval_pps:
  166. if timestamp<=row[0]:
  167. return row[1]
  168. return complement_interval_pps[-1][1] # in case the timstamp > capture max timestamp
  169. def getIpData(ip_address: str):
  170. """
  171. :param ip_address: the ip of which (packet-)data shall be returned
  172. :return: MSS, TTL and Window Size values of the given IP
  173. """
  174. # Set MSS (Maximum Segment Size) based on MSS distribution of IP address
  175. mss_dist = self.statistics.get_mss_distribution(ip_address)
  176. if len(mss_dist) > 0:
  177. mss_prob_dict = Lea.fromValFreqsDict(mss_dist)
  178. mss_value = mss_prob_dict.random()
  179. else:
  180. mss_value = self.statistics.process_db_query("most_used(mssValue)")
  181. # Set TTL based on TTL distribution of IP address
  182. ttl_dist = self.statistics.get_ttl_distribution(ip_address)
  183. if len(ttl_dist) > 0:
  184. ttl_prob_dict = Lea.fromValFreqsDict(ttl_dist)
  185. ttl_value = ttl_prob_dict.random()
  186. else:
  187. ttl_value = self.statistics.process_db_query("most_used(ttlValue)")
  188. # Set Window Size based on Window Size distribution of IP address
  189. win_dist = self.statistics.get_win_distribution(ip_address)
  190. if len(win_dist) > 0:
  191. win_prob_dict = Lea.fromValFreqsDict(win_dist)
  192. win_value = win_prob_dict.random()
  193. else:
  194. win_value = self.statistics.process_db_query("most_used(winSize)")
  195. return mss_value, ttl_value, win_value
  196. def getIpRange(start_ip: str, end_ip: str):
  197. start = ipaddress.ip_address(start_ip)
  198. end = ipaddress.ip_address(end_ip)
  199. ips = []
  200. if start < end:
  201. while start <= end:
  202. ips.append(start.exploded)
  203. start = start+1
  204. elif start > end:
  205. while start >= end:
  206. ips.append(start.exploded)
  207. start = start-1
  208. else:
  209. ips.append(start_ip)
  210. return ips
  211. def checkPlatform(platform: str):
  212. if platform not in self.platforms:
  213. print("\nERROR: Invalid platform: " + platform + "." +
  214. "\n Please select one of the following platforms: ", self.platforms)
  215. exit(1)
  216. def generateSourcePortFromPlatform(platform: str, previousPort=0):
  217. checkPlatform(platform)
  218. if platform in {"winnt", "winxp", "win2000"}:
  219. if (previousPort == 0) or (previousPort+1 > 5000):
  220. return randint(1024, 5000)
  221. else:
  222. return previousPort+1
  223. elif platform == "linux":
  224. return randint(32768, 61000)
  225. else:
  226. if (previousPort == 0) or (previousPort+1 > 65535):
  227. return randint(49152, 65535)
  228. else:
  229. return previousPort+1
  230. # FIXME: rework copy-pasted code
  231. # source: http://reliablybroken.com/b/2009/09/working-with-active-directory-filetime-values-in-python/
  232. # WORK IN PROGRESS
  233. def get_filetime_format(timestamp):
  234. EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time
  235. HUNDREDS_OF_NANOSECONDS = 10000000
  236. boot_datetime = datetime.fromtimestamp(timestamp)
  237. if (boot_datetime.tzinfo is None) or (boot_datetime.tzinfo.utcoffset(boot_datetime) is None):
  238. boot_datetime = boot_datetime.replace(tzinfo=boot_datetime.tzname())
  239. boot_filetime = EPOCH_AS_FILETIME + (timegm(boot_datetime.timetuple()) * HUNDREDS_OF_NANOSECONDS)
  240. return boot_filetime + (boot_datetime.microsecond * 10)
  241. def get_rnd_boot_time(timestamp, platform="winxp"):
  242. checkPlatform(platform)
  243. # FIXME: create probability distribution for each OS
  244. if platform is "linux":
  245. # four years
  246. timestamp -= randint(0, 126144000)
  247. if platform is "macOS":
  248. # three months
  249. timestamp -= randint(0, 7884000)
  250. else:
  251. # one month
  252. timestamp -= randint(0, 2678400)
  253. return get_filetime_format(timestamp)
  254. def getSmbPlatformData(platform: str, timestamp=time.time()):
  255. checkPlatform(platform)
  256. if platform == "linux":
  257. blob = self.security_blob_ubuntu
  258. capabilities = 0x5
  259. dataSize = 0x800000
  260. serverStartTime = 0
  261. elif platform == "macos":
  262. blob = self.security_blob_macos
  263. capabilities = 0x6
  264. dataSize = 0x400000
  265. serverStartTime = 0
  266. else:
  267. blob = self.security_blob_windows
  268. capabilities = 0x7
  269. dataSize = 0x100000
  270. serverStartTime = get_rnd_boot_time(timestamp)
  271. return blob, capabilities, dataSize, serverStartTime
  272. pps = self.get_param_value(Param.PACKETS_PER_SECOND)
  273. # Calculate complement packet rates of the background traffic for each interval
  274. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  275. # Timestamp
  276. timestamp_next_pkt = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
  277. # store start time of attack
  278. self.attack_start_utime = timestamp_next_pkt
  279. timestamp_prv_reply, timestamp_confirm = 0,0
  280. # Initialize parameters
  281. ip_source = self.get_param_value(Param.IP_SOURCE)
  282. ip_destinations = self.get_param_value(Param.IP_DESTINATION)
  283. hosting_ip = self.get_param_value(Param.HOSTING_IP)
  284. ip_range_end = self.get_param_value(Param.IP_DESTINATION_END)
  285. mac_source = self.get_param_value(Param.MAC_SOURCE)
  286. mac_dest = self.get_param_value(Param.MAC_DESTINATION)
  287. # Check smb version
  288. def invalid_version(version: str):
  289. print("\nInvalid smb version: " + version +
  290. "\nPlease select one of the following versions: ", self.smb_versions)
  291. exit(1)
  292. smb_version = self.get_param_value(Param.PROTOCOL_VERSION)
  293. if smb_version not in self.smb_versions:
  294. invalid_version(smb_version)
  295. hosting_version = self.get_param_value(Param.HOSTING_VERSION)
  296. if hosting_version not in self.smb_versions:
  297. invalid_version(hosting_version)
  298. # Check source platform
  299. src_platform = self.get_param_value(Param.SOURCE_PLATFORM).lower()
  300. packets = []
  301. # randomize source ports according to platform, if specified
  302. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  303. sport = generateSourcePortFromPlatform(src_platform)
  304. else:
  305. sport = self.get_param_value(Param.PORT_SOURCE)
  306. # No destination IP was specified, but a destination MAC was specified, generate IP that fits MAC
  307. if isinstance(ip_destinations, list) and isinstance(mac_dest, str):
  308. ip_destinations = self.statistics.get_ip_address_from_mac(mac_dest)
  309. if len(ip_destinations) == 0:
  310. ip_destinations = self.generate_random_ipv4_address("Unknown", 1)
  311. # Check ip.src == ip.dst
  312. self.ip_src_dst_equal_check(ip_source, ip_destinations)
  313. ip_dests = []
  314. if isinstance(ip_destinations, list):
  315. ip_dests = ip_destinations
  316. else:
  317. ip_dests.append(ip_destinations)
  318. # Generate IPs of destination IP range, if specified
  319. if ip_range_end != "0.0.0.0":
  320. ip_dests = getIpRange(ip_dests[0], ip_range_end)
  321. shuffle(ip_dests)
  322. # Randomize source IP, if specified
  323. if self.get_param_value(Param.IP_SOURCE_RANDOMIZE):
  324. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  325. while ip_source in ip_dests:
  326. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  327. mac_source = self.statistics.get_mac_address(str(ip_source))
  328. if len(mac_source) == 0:
  329. mac_source = self.generate_random_mac_address()
  330. # Get MSS, TTL and Window size value for source IP
  331. source_mss_value, source_ttl_value, source_win_value = getIpData(ip_source)
  332. for ip in ip_dests:
  333. if ip != ip_source:
  334. # Get destination Mac Address
  335. mac_destination = self.statistics.get_mac_address(str(ip))
  336. if len(mac_destination) == 0:
  337. if isinstance(mac_dest, str):
  338. if len(self.statistics.get_ip_address_from_mac(mac_dest)) != 0:
  339. ip = self.statistics.get_ip_address_from_mac(mac_dest)
  340. self.ip_src_dst_equal_check(ip_source, ip)
  341. mac_destination = mac_dest
  342. else:
  343. mac_destination = self.generate_random_mac_address()
  344. # Get MSS, TTL and Window size value for destination IP
  345. destination_mss_value, destination_ttl_value, destination_win_value = getIpData(ip)
  346. minDelay, maxDelay = self.get_reply_delay(ip)
  347. # New connection, new random TCP sequence numbers
  348. attacker_seq = randint(1000, 50000)
  349. victim_seq = randint(1000, 50000)
  350. # Randomize source port for each connection if specified
  351. if self.get_param_value(Param.PORT_SOURCE_RANDOMIZE):
  352. sport = generateSourcePortFromPlatform(src_platform, sport)
  353. # 1) Build request package
  354. request_ether = Ether(src=mac_source, dst=mac_destination)
  355. request_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value, flags='DF')
  356. request_tcp = TCP(sport=sport, dport=self.smb_port, window=source_win_value, flags='S',
  357. seq=attacker_seq, options=[('MSS', source_mss_value)])
  358. attacker_seq += 1
  359. request = (request_ether / request_ip / request_tcp)
  360. request.time = timestamp_next_pkt
  361. # Append request
  362. packets.append(request)
  363. # Update timestamp for next package
  364. timestamp_reply = update_timestamp(timestamp_next_pkt, pps, minDelay)
  365. while (timestamp_reply <= timestamp_prv_reply):
  366. timestamp_reply = update_timestamp(timestamp_prv_reply, pps, minDelay)
  367. timestamp_prv_reply = timestamp_reply
  368. if ip in hosting_ip:
  369. # 2) Build TCP packages for ip that hosts SMB
  370. # destination sends SYN, ACK
  371. reply_ether = Ether(src=mac_destination, dst=mac_source)
  372. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  373. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq, flags='SA',
  374. window=destination_win_value, options=[('MSS', destination_mss_value)])
  375. victim_seq += 1
  376. reply = (reply_ether / reply_ip / reply_tcp)
  377. reply.time = timestamp_reply
  378. packets.append(reply)
  379. # requester confirms, ACK
  380. confirm_ether = request_ether
  381. confirm_ip = request_ip
  382. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq,
  383. window=source_win_value, flags='A')
  384. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  385. timestamp_confirm = update_timestamp(timestamp_reply, pps, minDelay)
  386. confirm.time = timestamp_confirm
  387. packets.append(confirm)
  388. smb_MID = randint(1, 65535)
  389. smb_PID = randint(1, 65535)
  390. smb_req_tail_arr = []
  391. smb_req_tail_size = 0
  392. # select dialects based on smb version
  393. if smb_version is "1":
  394. smb_req_dialects = self.smb_dialects[0:6]
  395. else:
  396. smb_req_dialects = self.smb_dialects
  397. if len(smb_req_dialects) == 0:
  398. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail())
  399. smb_req_tail_size = len(SMBNegociate_Protocol_Request_Tail())
  400. else:
  401. for dia in smb_req_dialects:
  402. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
  403. smb_req_tail_size += len(SMBNegociate_Protocol_Request_Tail(BufferData = dia))
  404. smb_req_head = SMBNegociate_Protocol_Request_Header\
  405. (Flags2=0x2801, PID=smb_PID, MID=smb_MID, ByteCount=smb_req_tail_size)
  406. smb_req_length = len(smb_req_head) + smb_req_tail_size
  407. smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
  408. smb_req_tcp = TCP(sport=sport, dport=self.smb_port, flags='PA', seq=attacker_seq, ack=victim_seq)
  409. smb_req_ip = IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  410. smb_req_ether = Ether(src=mac_source, dst=mac_destination)
  411. attacker_seq += len(smb_req_net_bio) + len(smb_req_head) + smb_req_tail_size
  412. smb_req_combined = (smb_req_ether / smb_req_ip / smb_req_tcp / smb_req_net_bio / smb_req_head)
  413. for i in range(0 , len(smb_req_tail_arr)):
  414. smb_req_combined = smb_req_combined / smb_req_tail_arr[i]
  415. timestamp_smb_req = update_timestamp(timestamp_confirm, pps, minDelay)
  416. smb_req_combined.time = timestamp_smb_req
  417. packets.append(smb_req_combined)
  418. # destination confirms SMB request package
  419. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  420. window=destination_win_value, flags='A')
  421. confirm_smb_req = (reply_ether / reply_ip / reply_tcp)
  422. timestamp_reply = update_timestamp(timestamp_smb_req, pps, minDelay)
  423. confirm_smb_req.time = timestamp_reply
  424. packets.append(confirm_smb_req)
  425. # smb response package
  426. timestamp_smb_rsp = update_timestamp(timestamp_reply, pps, minDelay)
  427. security_blob, capabilities, dataSize, serverStartTime = getSmbPlatformData\
  428. (self.host_os, time.mktime(time.strptime(self.statistics.get_pcap_timestamp_start()[:19],
  429. "%Y-%m-%d %H:%M:%S")))
  430. diff = timestamp_smb_rsp - timestamp_smb_req
  431. begin = get_filetime_format(timestamp_smb_req+diff*0.1)
  432. end = get_filetime_format(timestamp_smb_rsp-diff*0.1)
  433. systemtime = randint(begin, end)
  434. if smb_version is not "1" and hosting_version is not "1":
  435. smb_rsp_paket = SMB2_SYNC_Header(Flags = 1)
  436. smb_rsp_negotiate_body = SMB2_Negotiate_Protocol_Response\
  437. (DialectRevision=0x02ff, SecurityBufferOffset=124, SecurityBufferLength=len(security_blob),
  438. SecurityBlob=security_blob, Capabilities=capabilities, MaxTransactSize=dataSize,
  439. MaxReadSize=dataSize, MaxWriteSize=dataSize, SystemTime=systemtime,
  440. ServerStartTime=serverStartTime)
  441. smb_rsp_length = len(smb_rsp_paket) + len(smb_rsp_negotiate_body)
  442. else:
  443. smb_rsp_paket = SMBNegociate_Protocol_Response_Advanced_Security\
  444. (Start="\xffSMB", PID=smb_PID, MID=smb_MID, DialectIndex=5, SecurityBlob=security_blob)
  445. smb_rsp_length = len(smb_rsp_paket)
  446. smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
  447. smb_rsp_tcp = TCP(sport=self.smb_port, dport=sport, flags='PA', seq=victim_seq, ack=attacker_seq)
  448. smb_rsp_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value)
  449. smb_rsp_ether = Ether(src=mac_destination, dst=mac_source)
  450. victim_seq += len(smb_rsp_net_bio) + len(smb_rsp_paket)
  451. if smb_version is not "1"and hosting_version is not "1":
  452. victim_seq += len(smb_rsp_negotiate_body)
  453. smb_rsp_combined = (smb_rsp_ether / smb_rsp_ip / smb_rsp_tcp / smb_rsp_net_bio / smb_rsp_paket)
  454. if smb_version is not "1"and hosting_version is not "1":
  455. smb_rsp_combined = (smb_rsp_combined / smb_rsp_negotiate_body)
  456. smb_rsp_combined.time = timestamp_smb_rsp
  457. packets.append(smb_rsp_combined)
  458. # source confirms SMB response package
  459. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq,
  460. window=source_win_value, flags='A')
  461. confirm_smb_res = (confirm_ether / confirm_ip / confirm_tcp)
  462. timestamp_confirm = update_timestamp(timestamp_smb_rsp, pps, minDelay)
  463. confirm_smb_res.time = timestamp_confirm
  464. packets.append(confirm_smb_res)
  465. # attacker sends FIN ACK
  466. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq,
  467. window=source_win_value, flags='FA')
  468. source_fin_ack = (confirm_ether / confirm_ip / confirm_tcp)
  469. timestamp_src_fin_ack = update_timestamp(timestamp_confirm, pps, minDelay)
  470. source_fin_ack.time = timestamp_src_fin_ack
  471. attacker_seq += 1
  472. packets.append(source_fin_ack)
  473. # victim sends FIN ACK
  474. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  475. window=destination_win_value, flags='FA')
  476. destination_fin_ack = (reply_ether / reply_ip / reply_tcp)
  477. timestamp_dest_fin_ack = update_timestamp(timestamp_src_fin_ack, pps, minDelay)
  478. victim_seq += 1
  479. destination_fin_ack.time = timestamp_dest_fin_ack
  480. packets.append(destination_fin_ack)
  481. # source sends final ACK
  482. confirm_tcp = TCP(sport=sport, dport=self.smb_port, seq=attacker_seq, ack=victim_seq,
  483. window=source_win_value, flags='A')
  484. final_ack = (confirm_ether / confirm_ip / confirm_tcp)
  485. timestamp_final_ack = update_timestamp(timestamp_dest_fin_ack, pps, minDelay)
  486. final_ack.time = timestamp_final_ack
  487. packets.append(final_ack)
  488. else:
  489. # Build RST package
  490. reply_ether = Ether(src=mac_destination, dst=mac_source)
  491. reply_ip = IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  492. reply_tcp = TCP(sport=self.smb_port, dport=sport, seq=0, ack=attacker_seq, flags='RA',
  493. window=destination_win_value, options=[('MSS', destination_mss_value)])
  494. reply = (reply_ether / reply_ip / reply_tcp)
  495. reply.time = timestamp_reply
  496. packets.append(reply)
  497. pps = max(getIntervalPPS(complement_interval_pps, timestamp_next_pkt), 10)
  498. timestamp_next_pkt = update_timestamp(timestamp_next_pkt, pps)
  499. # store end time of attack
  500. self.attack_end_utime = packets[-1].time
  501. # write attack packets to pcap
  502. pcap_path = self.write_attack_pcap(sorted(packets, key=lambda pkt: pkt.time))
  503. # return packets sorted by packet time_sec_start
  504. return len(packets), pcap_path