SMBScanAttack.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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.TARGET_COUNT: atkParam.ParameterTypes.TYPE_INTEGER_POSITIVE,
  29. atkParam.Parameter.HOSTING_PERCENTAGE: atkParam.ParameterTypes.TYPE_PERCENTAGE,
  30. atkParam.Parameter.PORT_SOURCE: atkParam.ParameterTypes.TYPE_PORT,
  31. atkParam.Parameter.MAC_SOURCE: atkParam.ParameterTypes.TYPE_MAC_ADDRESS,
  32. atkParam.Parameter.INJECT_AT_TIMESTAMP: atkParam.ParameterTypes.TYPE_FLOAT,
  33. atkParam.Parameter.INJECT_AFTER_PACKET: atkParam.ParameterTypes.TYPE_PACKET_POSITION,
  34. atkParam.Parameter.IP_SOURCE_RANDOMIZE: atkParam.ParameterTypes.TYPE_BOOLEAN,
  35. atkParam.Parameter.PACKETS_PER_SECOND: atkParam.ParameterTypes.TYPE_FLOAT,
  36. atkParam.Parameter.INJECT_PPS: atkParam.ParameterTypes.TYPE_FLOAT,
  37. atkParam.Parameter.PORT_SOURCE_RANDOMIZE: atkParam.ParameterTypes.TYPE_BOOLEAN,
  38. atkParam.Parameter.HOSTING_IP: atkParam.ParameterTypes.TYPE_IP_ADDRESS,
  39. atkParam.Parameter.HOSTING_VERSION: atkParam.ParameterTypes.TYPE_STRING,
  40. atkParam.Parameter.SOURCE_PLATFORM: atkParam.ParameterTypes.TYPE_STRING,
  41. atkParam.Parameter.PROTOCOL_VERSION: atkParam.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(atkParam.Parameter.IP_SOURCE, most_used_ip_address)
  53. self.add_param_value(atkParam.Parameter.IP_SOURCE_RANDOMIZE, 'False')
  54. self.add_param_value(atkParam.Parameter.MAC_SOURCE, self.statistics.get_mac_address(most_used_ip_address))
  55. self.add_param_value(atkParam.Parameter.TARGET_COUNT, 200)
  56. self.add_param_value(atkParam.Parameter.IP_DESTINATION, "1.1.1.1")
  57. self.add_param_value(atkParam.Parameter.PORT_SOURCE, rnd.randint(1024, 65535))
  58. self.add_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE, 'True')
  59. self.add_param_value(atkParam.Parameter.PACKETS_PER_SECOND,
  60. (self.statistics.get_pps_sent(most_used_ip_address) +
  61. self.statistics.get_pps_received(most_used_ip_address)) / 2)
  62. self.add_param_value(atkParam.Parameter.INJECT_AFTER_PACKET, rnd.randint(0, self.statistics.get_packet_count()))
  63. self.add_param_value(atkParam.Parameter.INJECT_PPS, 0)
  64. self.add_param_value(atkParam.Parameter.HOSTING_PERCENTAGE, 0.5)
  65. self.add_param_value(atkParam.Parameter.HOSTING_IP, "1.1.1.1")
  66. self.add_param_value(atkParam.Parameter.HOSTING_VERSION, SMBLib.get_smb_version(platform=self.host_os))
  67. self.add_param_value(atkParam.Parameter.SOURCE_PLATFORM, Util.get_rnd_os())
  68. self.add_param_value(atkParam.Parameter.PROTOCOL_VERSION, "1")
  69. def generate_attack_packets(self):
  70. """
  71. Creates the attack packets.
  72. """
  73. pps = self.get_param_value(atkParam.Parameter.PACKETS_PER_SECOND)
  74. # Calculate complement packet rates of the background traffic for each interval
  75. complement_interval_pps = self.statistics.calculate_complement_packet_rates(pps)
  76. # Timestamp
  77. timestamp_next_pkt = self.get_param_value(atkParam.Parameter.INJECT_AT_TIMESTAMP)
  78. # store start time of attack
  79. self.attack_start_utime = timestamp_next_pkt
  80. timestamp_prv_reply, timestamp_confirm = 0, 0
  81. # Initialize parameters
  82. ip_source = self.get_param_value(atkParam.Parameter.IP_SOURCE)
  83. dest_ip_count = self.get_param_value(atkParam.Parameter.TARGET_COUNT)
  84. ip_addr_count = self.statistics.get_ip_address_count()
  85. if ip_addr_count < dest_ip_count + 1:
  86. dest_ip_count = ip_addr_count
  87. # Check for user defined target IP addresses
  88. ip_destinations = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  89. if isinstance(ip_destinations, list):
  90. dest_ip_count = dest_ip_count - len(ip_destinations)
  91. elif ip_destinations is not "1.1.1.1":
  92. dest_ip_count = dest_ip_count - 1
  93. ip_destinations = [ip_destinations]
  94. else:
  95. ip_destinations = []
  96. # Take random targets from pcap
  97. rnd_ips = self.statistics.get_random_ip_address(dest_ip_count)
  98. if not isinstance(rnd_ips, list):
  99. rnd_ips = [rnd_ips]
  100. ip_destinations = ip_destinations + rnd_ips
  101. # Make sure the source IP is not part of targets
  102. if ip_source in ip_destinations and isinstance(ip_destinations, list):
  103. ip_destinations.remove(ip_source)
  104. self.add_param_value(atkParam.Parameter.IP_DESTINATION, ip_destinations)
  105. ip_destinations = self.get_param_value(atkParam.Parameter.IP_DESTINATION)
  106. # Calculate the amount of IP addresses which are hosting SMB
  107. host_percentage = self.get_param_value(atkParam.Parameter.HOSTING_PERCENTAGE)
  108. rnd_ip_count = len(ip_destinations) * host_percentage
  109. # Check for user defined IP addresses which are hosting SMB
  110. hosting_ip = self.get_param_value(atkParam.Parameter.HOSTING_IP)
  111. if isinstance(hosting_ip, list):
  112. rnd_ip_count = rnd_ip_count - len(hosting_ip)
  113. elif hosting_ip is not "1.1.1.1":
  114. rnd_ip_count = rnd_ip_count - 1
  115. hosting_ip = [hosting_ip]
  116. else:
  117. hosting_ip = []
  118. hosting_ip = hosting_ip + ip_destinations[:int(rnd_ip_count)]
  119. self.add_param_value(atkParam.Parameter.HOSTING_IP, hosting_ip)
  120. # Shuffle targets
  121. rnd.shuffle(ip_destinations)
  122. # FIXME: Handle mac addresses correctly
  123. mac_source = self.get_param_value(atkParam.Parameter.MAC_SOURCE)
  124. mac_dest = self.get_param_value(atkParam.Parameter.MAC_DESTINATION)
  125. # Check smb version
  126. smb_version = self.get_param_value(atkParam.Parameter.PROTOCOL_VERSION)
  127. if smb_version not in SMBLib.smb_versions:
  128. SMBLib.invalid_smb_version(smb_version)
  129. hosting_version = self.get_param_value(atkParam.Parameter.HOSTING_VERSION)
  130. if hosting_version not in SMBLib.smb_versions:
  131. SMBLib.invalid_smb_version(hosting_version)
  132. # Check source platform
  133. src_platform = self.get_param_value(atkParam.Parameter.SOURCE_PLATFORM).lower()
  134. self.packets = []
  135. # randomize source ports according to platform, if specified
  136. if self.get_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE):
  137. sport = Util.generate_source_port_from_platform(src_platform)
  138. else:
  139. sport = self.get_param_value(atkParam.Parameter.PORT_SOURCE)
  140. # No destination IP was specified, but a destination MAC was specified, generate IP that fits MAC
  141. if isinstance(ip_destinations, list) and isinstance(mac_dest, str):
  142. ip_destinations = self.statistics.get_ip_address_from_mac(mac_dest)
  143. if len(ip_destinations) == 0:
  144. ip_destinations = self.generate_random_ipv4_address("Unknown", 1)
  145. # Check ip.src == ip.dst
  146. self.ip_src_dst_equal_check(ip_source, ip_destinations)
  147. ip_dests = []
  148. if isinstance(ip_destinations, list):
  149. ip_dests = ip_destinations
  150. else:
  151. ip_dests.append(ip_destinations)
  152. if isinstance(ip_dests, list):
  153. rnd.shuffle(ip_dests)
  154. # Randomize source IP, if specified
  155. if self.get_param_value(atkParam.Parameter.IP_SOURCE_RANDOMIZE):
  156. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  157. while ip_source in ip_dests:
  158. ip_source = self.generate_random_ipv4_address("Unknown", 1)
  159. mac_source = self.statistics.get_mac_address(str(ip_source))
  160. if len(mac_source) == 0:
  161. mac_source = self.generate_random_mac_address()
  162. # Get MSS, TTL and Window size value for source IP
  163. source_mss_value, source_ttl_value, source_win_value = self.get_ip_data(ip_source)
  164. mac_dests = self.statistics.get_mac_addresses(ip_dests)
  165. first_timestamp_smb = self.statistics.get_pcap_timestamp_start()[:19]
  166. # get inject pss
  167. inject_pps = self.get_param_value(atkParam.Parameter.INJECT_PPS)
  168. for ip in ip_dests:
  169. if ip != ip_source:
  170. # Get destination Mac Address
  171. mac_destination = ""
  172. if ip in mac_dests.keys():
  173. mac_destination = mac_dests[ip]
  174. if len(mac_destination) == 0:
  175. if isinstance(mac_dest, str):
  176. ip_from_mac = self.statistics.get_ip_address_from_mac(mac_dest)
  177. if len(ip_from_mac) != 0:
  178. ip = ip_from_mac
  179. self.ip_src_dst_equal_check(ip_source, ip)
  180. mac_destination = mac_dest
  181. else:
  182. mac_destination = self.generate_random_mac_address()
  183. # Get MSS, TTL and Window size value for destination IP
  184. destination_mss_value, destination_ttl_value, destination_win_value = self.get_ip_data(ip)
  185. min_delay, max_delay = self.get_reply_delay(ip)
  186. # New connection, new random TCP sequence numbers
  187. attacker_seq = rnd.randint(1000, 50000)
  188. victim_seq = rnd.randint(1000, 50000)
  189. # Randomize source port for each connection if specified
  190. if self.get_param_value(atkParam.Parameter.PORT_SOURCE_RANDOMIZE):
  191. sport = Util.generate_source_port_from_platform(src_platform, sport)
  192. # 1) Build request package
  193. request_ether = inet.Ether(src=mac_source, dst=mac_destination)
  194. request_ip = inet.IP(src=ip_source, dst=ip, ttl=source_ttl_value, flags='DF')
  195. request_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, window=source_win_value, flags='S',
  196. seq=attacker_seq, options=[('MSS', source_mss_value)])
  197. attacker_seq += 1
  198. request = (request_ether / request_ip / request_tcp)
  199. request.time = timestamp_next_pkt
  200. # Append request
  201. self.packets.append(request)
  202. # Update timestamp for next package
  203. timestamp_reply = Util.update_timestamp(timestamp_next_pkt, pps, min_delay, inj_pps=inject_pps,
  204. inj_timestamp=self.attack_start_utime)
  205. while timestamp_reply <= timestamp_prv_reply:
  206. timestamp_reply = Util.update_timestamp(timestamp_prv_reply, pps, min_delay, inj_pps=inject_pps,
  207. inj_timestamp=self.attack_start_utime)
  208. timestamp_prv_reply = timestamp_reply
  209. if ip in hosting_ip:
  210. # 2) Build TCP packages for ip that hosts SMB
  211. # destination sends SYN, ACK
  212. reply_ether = inet.Ether(src=mac_destination, dst=mac_source)
  213. reply_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  214. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  215. flags='SA',
  216. window=destination_win_value, options=[('MSS', destination_mss_value)])
  217. victim_seq += 1
  218. reply = (reply_ether / reply_ip / reply_tcp)
  219. reply.time = timestamp_reply
  220. self.packets.append(reply)
  221. # requester confirms, ACK
  222. confirm_ether = request_ether
  223. confirm_ip = request_ip
  224. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  225. window=source_win_value, flags='A')
  226. confirm = (confirm_ether / confirm_ip / confirm_tcp)
  227. timestamp_confirm = Util.update_timestamp(timestamp_reply, pps, min_delay)
  228. confirm.time = timestamp_confirm
  229. self.packets.append(confirm)
  230. # 3) Build SMB Negotiation packets
  231. smb_mid = rnd.randint(1, 65535)
  232. smb_pid = rnd.randint(1, 65535)
  233. smb_req_tail_arr = []
  234. smb_req_tail_size = 0
  235. # select dialects based on smb version
  236. if smb_version is "1":
  237. smb_req_dialects = SMBLib.smb_dialects[0:6]
  238. else:
  239. smb_req_dialects = SMBLib.smb_dialects
  240. if len(smb_req_dialects) == 0:
  241. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail())
  242. smb_req_tail_size = len(SMBNegociate_Protocol_Request_Tail())
  243. else:
  244. for dia in smb_req_dialects:
  245. smb_req_tail_arr.append(SMBNegociate_Protocol_Request_Tail(BufferData=dia))
  246. smb_req_tail_size += len(SMBNegociate_Protocol_Request_Tail(BufferData=dia))
  247. # Creation of SMB Negotiate Protocol Request packet
  248. smb_req_head = SMBNegociate_Protocol_Request_Header(Flags2=0x2801, PID=smb_pid, MID=smb_mid,
  249. ByteCount=smb_req_tail_size)
  250. smb_req_length = len(smb_req_head) + smb_req_tail_size
  251. smb_req_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_req_length)
  252. smb_req_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, flags='PA', seq=attacker_seq,
  253. ack=victim_seq)
  254. smb_req_ip = inet.IP(src=ip_source, dst=ip, ttl=source_ttl_value)
  255. smb_req_ether = inet.Ether(src=mac_source, dst=mac_destination)
  256. attacker_seq += len(smb_req_net_bio) + len(smb_req_head) + smb_req_tail_size
  257. smb_req_combined = (smb_req_ether / smb_req_ip / smb_req_tcp / smb_req_net_bio / smb_req_head)
  258. for i in range(0, len(smb_req_tail_arr)):
  259. smb_req_combined = smb_req_combined / smb_req_tail_arr[i]
  260. timestamp_smb_req = Util.update_timestamp(timestamp_confirm, pps, min_delay)
  261. smb_req_combined.time = timestamp_smb_req
  262. self.packets.append(smb_req_combined)
  263. # destination confirms SMB request package
  264. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  265. window=destination_win_value, flags='A')
  266. confirm_smb_req = (reply_ether / reply_ip / reply_tcp)
  267. timestamp_reply = Util.update_timestamp(timestamp_smb_req, pps, min_delay)
  268. confirm_smb_req.time = timestamp_reply
  269. self.packets.append(confirm_smb_req)
  270. # smb response package
  271. first_timestamp = time.mktime(time.strptime(first_timestamp_smb, "%Y-%m-%d %H:%M:%S"))
  272. server_guid, security_blob, capabilities, data_size, server_start_time =\
  273. SMBLib.get_smb_platform_data(self.host_os, first_timestamp)
  274. timestamp_smb_rsp = Util.update_timestamp(timestamp_reply, pps, min_delay)
  275. diff = timestamp_smb_rsp - timestamp_smb_req
  276. begin = Util.get_filetime_format(timestamp_smb_req + diff * 0.1)
  277. end = Util.get_filetime_format(timestamp_smb_rsp - diff * 0.1)
  278. system_time = rnd.randint(begin, end)
  279. # Creation of SMB Negotiate Protocol Response packets
  280. if smb_version is not "1" and hosting_version is not "1":
  281. smb_rsp_packet = SMB2.SMB2_SYNC_Header(Flags=1)
  282. smb_rsp_negotiate_body =\
  283. SMB2.SMB2_Negotiate_Protocol_Response(DialectRevision=0x02ff, SecurityBufferOffset=124,
  284. SecurityBufferLength=len(security_blob),
  285. SecurityBlob=security_blob, Capabilities=capabilities,
  286. MaxTransactSize=data_size, MaxReadSize=data_size,
  287. MaxWriteSize=data_size, SystemTime=system_time,
  288. ServerStartTime=server_start_time,
  289. ServerGuid=server_guid)
  290. smb_rsp_length = len(smb_rsp_packet) + len(smb_rsp_negotiate_body)
  291. else:
  292. smb_rsp_packet =\
  293. SMBNegociate_Protocol_Response_Advanced_Security(Start="\xffSMB", PID=smb_pid, MID=smb_mid,
  294. DialectIndex=5, SecurityBlob=security_blob)
  295. smb_rsp_length = len(smb_rsp_packet)
  296. smb_rsp_net_bio = NBTSession(TYPE=0x00, LENGTH=smb_rsp_length)
  297. smb_rsp_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, flags='PA', seq=victim_seq,
  298. ack=attacker_seq)
  299. smb_rsp_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value)
  300. smb_rsp_ether = inet.Ether(src=mac_destination, dst=mac_source)
  301. victim_seq += len(smb_rsp_net_bio) + len(smb_rsp_packet)
  302. if smb_version is not "1" and hosting_version is not "1":
  303. victim_seq += len(smb_rsp_negotiate_body)
  304. smb_rsp_combined = (smb_rsp_ether / smb_rsp_ip / smb_rsp_tcp / smb_rsp_net_bio / smb_rsp_packet)
  305. if smb_version is not "1" and hosting_version is not "1":
  306. smb_rsp_combined = (smb_rsp_combined / smb_rsp_negotiate_body)
  307. smb_rsp_combined.time = timestamp_smb_rsp
  308. self.packets.append(smb_rsp_combined)
  309. # source confirms SMB response package
  310. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  311. window=source_win_value, flags='A')
  312. confirm_smb_res = (confirm_ether / confirm_ip / confirm_tcp)
  313. timestamp_confirm = Util.update_timestamp(timestamp_smb_rsp, pps, min_delay)
  314. confirm_smb_res.time = timestamp_confirm
  315. self.packets.append(confirm_smb_res)
  316. # attacker sends FIN ACK
  317. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  318. window=source_win_value, flags='FA')
  319. source_fin_ack = (confirm_ether / confirm_ip / confirm_tcp)
  320. timestamp_src_fin_ack = Util.update_timestamp(timestamp_confirm, pps, min_delay)
  321. source_fin_ack.time = timestamp_src_fin_ack
  322. attacker_seq += 1
  323. self.packets.append(source_fin_ack)
  324. # victim sends FIN ACK
  325. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=victim_seq, ack=attacker_seq,
  326. window=destination_win_value, flags='FA')
  327. destination_fin_ack = (reply_ether / reply_ip / reply_tcp)
  328. timestamp_dest_fin_ack = Util.update_timestamp(timestamp_src_fin_ack, pps, min_delay)
  329. victim_seq += 1
  330. destination_fin_ack.time = timestamp_dest_fin_ack
  331. self.packets.append(destination_fin_ack)
  332. # source sends final ACK
  333. confirm_tcp = inet.TCP(sport=sport, dport=SMBLib.smb_port, seq=attacker_seq, ack=victim_seq,
  334. window=source_win_value, flags='A')
  335. final_ack = (confirm_ether / confirm_ip / confirm_tcp)
  336. timestamp_final_ack = Util.update_timestamp(timestamp_dest_fin_ack, pps, min_delay)
  337. final_ack.time = timestamp_final_ack
  338. self.packets.append(final_ack)
  339. else:
  340. # Build RST package
  341. reply_ether = inet.Ether(src=mac_destination, dst=mac_source)
  342. reply_ip = inet.IP(src=ip, dst=ip_source, ttl=destination_ttl_value, flags='DF')
  343. reply_tcp = inet.TCP(sport=SMBLib.smb_port, dport=sport, seq=0, ack=attacker_seq, flags='RA',
  344. window=destination_win_value, options=[('MSS', destination_mss_value)])
  345. reply = (reply_ether / reply_ip / reply_tcp)
  346. reply.time = timestamp_reply
  347. self.packets.append(reply)
  348. pps = max(Util.get_interval_pps(complement_interval_pps, timestamp_next_pkt), 10)
  349. timestamp_next_pkt = Util.update_timestamp(timestamp_next_pkt, pps)
  350. def generate_attack_pcap(self):
  351. """
  352. Creates a pcap containing the attack packets.
  353. :return: The location of the generated pcap file.
  354. """
  355. # store end time of attack
  356. self.attack_end_utime = self.packets[-1].time
  357. # write attack self.packets to pcap
  358. pcap_path = self.write_attack_pcap(sorted(self.packets, key=lambda pkt: pkt.time))
  359. # return packets sorted by packet time_sec_start
  360. return len(self.packets), pcap_path