dissect.target 3.17.dev25__py3-none-any.whl → 3.17.dev27__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ import re
5
5
  from collections import defaultdict
6
6
  from configparser import ConfigParser, MissingSectionHeaderError
7
7
  from io import StringIO
8
+ from itertools import chain
8
9
  from re import compile, sub
9
10
  from typing import Any, Callable, Iterable, Match, Optional
10
11
 
@@ -299,7 +300,8 @@ class Parser:
299
300
  return
300
301
 
301
302
  if section:
302
- config = config.get(section, {})
303
+ # account for values of sections which are None
304
+ config = config.get(section, {}) or {}
303
305
 
304
306
  for key, value in config.items():
305
307
  if key == option:
@@ -508,7 +510,7 @@ class LinuxNetworkManager:
508
510
 
509
511
 
510
512
  def parse_unix_dhcp_log_messages(target) -> list[str]:
511
- """Parse local syslog and cloud init log files for DHCP lease IPs.
513
+ """Parse local syslog, journal and cloud init-log files for DHCP lease IPs.
512
514
 
513
515
  Args:
514
516
  target: Target to discover and obtain network information from.
@@ -516,53 +518,68 @@ def parse_unix_dhcp_log_messages(target) -> list[str]:
516
518
  Returns:
517
519
  List of DHCP ip addresses.
518
520
  """
519
- ips = []
520
-
521
- # Search through parsed syslogs for DHCP leases.
522
- try:
523
- messages = target.messages()
524
- for record in messages:
525
- line = record.message
526
-
527
- # Ubuntu DHCP
528
- if ("DHCPv4" in line or "DHCPv6" in line) and " address " in line and " via " in line:
529
- ip = line.split(" address ")[1].split(" via ")[0].strip().split("/")[0]
530
- if ip not in ips:
531
- ips.append(ip)
532
-
533
- # Ubuntu DHCP NetworkManager
534
- elif "option ip_address" in line and ("dhcp4" in line or "dhcp6" in line) and "=> '" in line:
535
- ip = line.split("=> '")[1].replace("'", "").strip()
536
- if ip not in ips:
537
- ips.append(ip)
538
-
539
- # Debian and CentOS dhclient
540
- elif record.daemon == "dhclient" and "bound to" in line:
541
- ip = line.split("bound to")[1].split(" ")[1].strip()
542
- if ip not in ips:
543
- ips.append(ip)
544
-
545
- # CentOS DHCP and general NetworkManager
546
- elif " address " in line and ("dhcp4" in line or "dhcp6" in line):
547
- ip = line.split(" address ")[1].strip()
548
- if ip not in ips:
549
- ips.append(ip)
550
-
551
- except PluginError:
552
- target.log.debug("Can not search for DHCP leases in syslog files as they does not exist.")
553
-
554
- # A unix system might be provisioned using Ubuntu's cloud-init (https://cloud-init.io/).
555
- if (path := target.fs.path("/var/log/cloud-init.log")).exists():
556
- for line in path.open("rt"):
557
- # We are interested in the following log line:
558
- # YYYY-MM-DD HH:MM:SS,000 - dhcp.py[DEBUG]: Received dhcp lease on IFACE for IP/MASK
559
- if "Received dhcp lease on" in line:
560
- interface, ip, netmask = re.search(r"Received dhcp lease on (\w{0,}) for (\S+)\/(\S+)", line).groups()
561
- if ip not in ips:
562
- ips.append(ip)
563
-
564
- if not path and not messages:
565
- target.log.warning("Can not search for DHCP leases in syslog or cloud-init.log files as they does not exist.")
521
+ ips = set()
522
+ messages = set()
523
+
524
+ for log_func in ["messages", "journal"]:
525
+ try:
526
+ messages = chain(messages, getattr(target, log_func)())
527
+ except PluginError:
528
+ target.log.debug(f"Could not search for DHCP leases in {log_func} files.")
529
+
530
+ if not messages:
531
+ target.log.warning(f"Could not search for DHCP leases using {log_func}: No log entries found.")
532
+
533
+ for record in messages:
534
+ line = record.message
535
+
536
+ # Ubuntu cloud-init
537
+ if "Received dhcp lease on" in line:
538
+ interface, ip, netmask = re.search(r"Received dhcp lease on (\w{0,}) for (\S+)\/(\S+)", line).groups()
539
+ ips.add(ip)
540
+ continue
541
+
542
+ # Ubuntu DHCP
543
+ if ("DHCPv4" in line or "DHCPv6" in line) and " address " in line and " via " in line:
544
+ ip = line.split(" address ")[1].split(" via ")[0].strip().split("/")[0]
545
+ ips.add(ip)
546
+ continue
547
+
548
+ # Ubuntu DHCP NetworkManager
549
+ if "option ip_address" in line and ("dhcp4" in line or "dhcp6" in line) and "=> '" in line:
550
+ ip = line.split("=> '")[1].replace("'", "").strip()
551
+ ips.add(ip)
552
+ continue
553
+
554
+ # Debian and CentOS dhclient
555
+ if hasattr(record, "daemon") and record.daemon == "dhclient" and "bound to" in line:
556
+ ip = line.split("bound to")[1].split(" ")[1].strip()
557
+ ips.add(ip)
558
+ continue
559
+
560
+ # CentOS DHCP and general NetworkManager
561
+ if " address " in line and ("dhcp4" in line or "dhcp6" in line):
562
+ ip = line.split(" address ")[1].strip()
563
+ ips.add(ip)
564
+ continue
565
+
566
+ # Ubuntu/Debian DHCP networkd (Journal)
567
+ if (
568
+ hasattr(record, "code_func")
569
+ and record.code_func == "dhcp_lease_acquired"
570
+ and " address " in line
571
+ and " via " in line
572
+ ):
573
+ interface, ip, netmask, gateway = re.search(
574
+ r"^(\S+): DHCPv[4|6] address (\S+)\/(\S+) via (\S+)", line
575
+ ).groups()
576
+ ips.add(ip)
577
+ continue
578
+
579
+ # Journals and syslogs can be large and slow to iterate,
580
+ # so we stop if we have some results and have reached the journal plugin.
581
+ if len(ips) >= 2 and record._desc.name == "linux/log/journal":
582
+ break
566
583
 
567
584
  return ips
568
585
 
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import re
2
3
  from itertools import product
3
4
  from pathlib import Path
@@ -14,6 +15,7 @@ from dissect.target.plugins.apps.ssh.ssh import (
14
15
  PrivateKeyRecord,
15
16
  PublicKeyRecord,
16
17
  SSHPlugin,
18
+ calculate_fingerprints,
17
19
  )
18
20
 
19
21
 
@@ -143,12 +145,14 @@ class OpenSSHPlugin(SSHPlugin):
143
145
  continue
144
146
 
145
147
  key_type, public_key, comment = parse_ssh_public_key_file(file_path)
148
+ fingerprints = calculate_fingerprints(base64.b64decode(public_key))
146
149
 
147
150
  yield PublicKeyRecord(
148
151
  mtime_ts=file_path.stat().st_mtime,
149
152
  key_type=key_type,
150
153
  public_key=public_key,
151
154
  comment=comment,
155
+ fingerprint=fingerprints,
152
156
  path=file_path,
153
157
  _target=self.target,
154
158
  _user=user,
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from base64 import b64decode
2
3
  from datetime import datetime
3
4
  from pathlib import Path
4
5
  from typing import Iterator, Optional, Union
@@ -12,7 +13,11 @@ from dissect.target.helpers.fsutil import TargetPath, open_decompress
12
13
  from dissect.target.helpers.record import create_extended_descriptor
13
14
  from dissect.target.helpers.regutil import RegistryKey
14
15
  from dissect.target.plugin import export
15
- from dissect.target.plugins.apps.ssh.ssh import KnownHostRecord, SSHPlugin
16
+ from dissect.target.plugins.apps.ssh.ssh import (
17
+ KnownHostRecord,
18
+ SSHPlugin,
19
+ calculate_fingerprints,
20
+ )
16
21
  from dissect.target.plugins.general.users import UserDetails
17
22
 
18
23
  log = logging.getLogger(__name__)
@@ -96,12 +101,15 @@ class PuTTYPlugin(SSHPlugin):
96
101
  key_type, host = entry.name.split("@")
97
102
  port, host = host.split(":")
98
103
 
104
+ public_key, fingerprints = construct_public_key(key_type, entry.value)
105
+
99
106
  yield KnownHostRecord(
100
107
  mtime_ts=ssh_host_keys.ts,
101
108
  host=host,
102
109
  port=port,
103
110
  key_type=key_type,
104
- public_key=construct_public_key(key_type, entry.value),
111
+ public_key=public_key,
112
+ fingerprint=fingerprints,
105
113
  comment="",
106
114
  marker=None,
107
115
  path=windows_path(ssh_host_keys.path),
@@ -121,12 +129,15 @@ class PuTTYPlugin(SSHPlugin):
121
129
  key_type, host = parts[0].split("@")
122
130
  port, host = host.split(":")
123
131
 
132
+ public_key, fingerprints = construct_public_key(key_type, parts[1])
133
+
124
134
  yield KnownHostRecord(
125
135
  mtime_ts=ts,
126
136
  host=host,
127
137
  port=port,
128
138
  key_type=key_type,
129
- public_key=construct_public_key(key_type, parts[1]),
139
+ public_key=public_key,
140
+ fingerprint=fingerprints,
130
141
  comment="",
131
142
  marker=None,
132
143
  path=posix_path(ssh_host_keys_path),
@@ -197,8 +208,8 @@ def parse_host_user(host: str, user: str) -> tuple[str, str]:
197
208
  return host, user
198
209
 
199
210
 
200
- def construct_public_key(key_type: str, iv: str) -> str:
201
- """Returns OpenSSH format public key calculated from PuTTY SshHostKeys format.
211
+ def construct_public_key(key_type: str, iv: str) -> tuple[str, tuple[str, str, str]]:
212
+ """Returns OpenSSH format public key calculated from PuTTY SshHostKeys format and set of fingerprints.
202
213
 
203
214
  PuTTY stores raw public key components instead of OpenSSH-formatted public keys
204
215
  or fingerprints. With RSA public keys the exponent and modulus are stored.
@@ -206,9 +217,7 @@ def construct_public_key(key_type: str, iv: str) -> str:
206
217
 
207
218
  Currently supports ``ssh-ed25519``, ``ecdsa-sha2-nistp256`` and ``rsa2`` key types.
208
219
 
209
- NOTE:
210
- - Sha256 fingerprints of the reconstructed public keys are currently not generated.
211
- - More key types could be supported in the future.
220
+ NOTE: More key types could be supported in the future.
212
221
 
213
222
  Resources:
214
223
  - https://github.com/github/putty/blob/master/contrib/kh2reg.py
@@ -217,20 +226,33 @@ def construct_public_key(key_type: str, iv: str) -> str:
217
226
  - https://github.com/mkorthof/reg2kh
218
227
  """
219
228
 
229
+ if not isinstance(key_type, str) or not isinstance(iv, str):
230
+ raise ValueError("Invalid key_type or iv")
231
+
232
+ key = None
233
+
220
234
  if key_type == "ssh-ed25519":
221
235
  x, y = iv.split(",")
222
236
  key = ECC.construct(curve="ed25519", point_x=int(x, 16), point_y=int(y, 16))
223
- return key.public_key().export_key(format="OpenSSH").split()[-1]
224
237
 
225
238
  if key_type == "ecdsa-sha2-nistp256":
226
239
  _, x, y = iv.split(",")
227
240
  key = ECC.construct(curve="NIST P-256", point_x=int(x, 16), point_y=int(y, 16))
228
- return key.public_key().export_key(format="OpenSSH").split()[-1]
229
241
 
230
242
  if key_type == "rsa2":
231
243
  exponent, modulus = iv.split(",")
232
244
  key = RSA.construct((int(modulus, 16), int(exponent, 16)))
233
- return key.public_key().export_key(format="OpenSSH").decode("utf-8").split()[-1]
234
245
 
235
- log.warning("Could not reconstruct public key: type %s not implemented.", key_type)
236
- return iv
246
+ if key is None:
247
+ log.warning("Could not reconstruct public key: type %s not implemented", key_type)
248
+ return iv, (None, None, None)
249
+
250
+ openssh_public_key = key.public_key().export_key(format="OpenSSH")
251
+
252
+ if isinstance(openssh_public_key, bytes):
253
+ # RSA's export_key() returns bytes
254
+ openssh_public_key = openssh_public_key.decode()
255
+
256
+ key_part = openssh_public_key.split()[-1]
257
+ fingerprints = calculate_fingerprints(b64decode(key_part))
258
+ return key_part, fingerprints
@@ -1,3 +1,6 @@
1
+ import base64
2
+ from hashlib import md5, sha1, sha256
3
+
1
4
  from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
2
5
  from dissect.target.helpers.record import create_extended_descriptor
3
6
  from dissect.target.plugin import NamespacePlugin
@@ -29,6 +32,7 @@ KnownHostRecord = OpenSSHUserRecordDescriptor(
29
32
  ("varint", "port"),
30
33
  ("string", "public_key"),
31
34
  ("string", "marker"),
35
+ ("digest", "fingerprint"),
32
36
  ],
33
37
  )
34
38
 
@@ -50,9 +54,45 @@ PublicKeyRecord = OpenSSHUserRecordDescriptor(
50
54
  ("datetime", "mtime_ts"),
51
55
  *COMMON_ELLEMENTS,
52
56
  ("string", "public_key"),
57
+ ("digest", "fingerprint"),
53
58
  ],
54
59
  )
55
60
 
56
61
 
57
62
  class SSHPlugin(NamespacePlugin):
58
63
  __namespace__ = "ssh"
64
+
65
+
66
+ def calculate_fingerprints(public_key_decoded: bytes, ssh_keygen_format: bool = False) -> tuple[str, str, str]:
67
+ """Calculate the MD5, SHA1 and SHA256 digest of the given decoded public key.
68
+
69
+ Adheres as much as possible to the output provided by ssh-keygen when ``ssh_keygen_format``
70
+ parameter is set to ``True``. When set to ``False`` (default) hexdigests are calculated
71
+ instead for ``sha1``and ``sha256``.
72
+
73
+ Resources:
74
+ - https://en.wikipedia.org/wiki/Public_key_fingerprint
75
+ - https://man7.org/linux/man-pages/man1/ssh-keygen.1.html
76
+ - ``ssh-keygen -l -E <alg> -f key.pub``
77
+ """
78
+ if not public_key_decoded:
79
+ raise ValueError("No decoded public key provided")
80
+
81
+ if not isinstance(public_key_decoded, bytes):
82
+ raise ValueError("Provided public key should be bytes")
83
+
84
+ if public_key_decoded[0:3] != b"\x00\x00\x00":
85
+ raise ValueError("Provided value does not look like a public key")
86
+
87
+ digest_md5 = md5(public_key_decoded).digest()
88
+ digest_sha1 = sha1(public_key_decoded).digest()
89
+ digest_sha256 = sha256(public_key_decoded).digest()
90
+
91
+ if ssh_keygen_format:
92
+ fingerprint_sha1 = base64.b64encode(digest_sha1).rstrip(b"=").decode()
93
+ fingerprint_sha256 = base64.b64encode(digest_sha256).rstrip(b"=").decode()
94
+ else:
95
+ fingerprint_sha1 = digest_sha1.hex()
96
+ fingerprint_sha256 = digest_sha256.hex()
97
+
98
+ return digest_md5.hex(), fingerprint_sha1, fingerprint_sha256
@@ -1,7 +1,8 @@
1
1
  import re
2
- from itertools import chain
2
+ from pathlib import Path
3
3
  from typing import Iterator
4
4
 
5
+ from dissect.target import Target
5
6
  from dissect.target.exceptions import UnsupportedPluginError
6
7
  from dissect.target.helpers.record import TargetRecordDescriptor
7
8
  from dissect.target.helpers.utils import year_rollover_helper
@@ -23,17 +24,28 @@ RE_TS = re.compile(r"(\w+\s{1,2}\d+\s\d{2}:\d{2}:\d{2})")
23
24
  RE_DAEMON = re.compile(r"^[^:]+:\d+:\d+[^\[\]:]+\s([^\[:]+)[\[|:]{1}")
24
25
  RE_PID = re.compile(r"\w\[(\d+)\]")
25
26
  RE_MSG = re.compile(r"[^:]+:\d+:\d+[^:]+:\s(.*)$")
27
+ RE_CLOUD_INIT_LINE = re.compile(r"(?P<ts>.*) - (?P<daemon>.*)\[(?P<log_level>\w+)\]\: (?P<message>.*)$")
26
28
 
27
29
 
28
30
  class MessagesPlugin(Plugin):
31
+ def __init__(self, target: Target):
32
+ super().__init__(target)
33
+ self.log_files = set(self._find_log_files())
34
+
35
+ def _find_log_files(self) -> Iterator[Path]:
36
+ log_dirs = ["/var/log/", "/var/log/installer/"]
37
+ file_globs = ["syslog*", "messages*", "cloud-init.log*"]
38
+ for log_dir in log_dirs:
39
+ for glob in file_globs:
40
+ yield from self.target.fs.path(log_dir).glob(glob)
41
+
29
42
  def check_compatible(self) -> None:
30
- var_log = self.target.fs.path("/var/log")
31
- if not any(var_log.glob("syslog*")) and not any(var_log.glob("messages*")):
32
- raise UnsupportedPluginError("No message files found")
43
+ if not self.log_files:
44
+ raise UnsupportedPluginError("No log files found")
33
45
 
34
46
  @export(record=MessagesRecord)
35
47
  def syslog(self) -> Iterator[MessagesRecord]:
36
- """Return contents of /var/log/messages* and /var/log/syslog*.
48
+ """Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
37
49
 
38
50
  See ``messages`` for more information.
39
51
  """
@@ -41,7 +53,7 @@ class MessagesPlugin(Plugin):
41
53
 
42
54
  @export(record=MessagesRecord)
43
55
  def messages(self) -> Iterator[MessagesRecord]:
44
- """Return contents of /var/log/messages* and /var/log/syslog*.
56
+ """Return contents of /var/log/messages*, /var/log/syslog* and cloud-init logs.
45
57
 
46
58
  Note: due to year rollover detection, the contents of the files are returned in reverse.
47
59
 
@@ -52,12 +64,16 @@ class MessagesPlugin(Plugin):
52
64
  References:
53
65
  - https://geek-university.com/linux/var-log-messages-file/
54
66
  - https://www.geeksforgeeks.org/file-timestamps-mtime-ctime-and-atime-in-linux/
67
+ - https://cloudinit.readthedocs.io/en/latest/development/logging.html#logging-command-output
55
68
  """
56
69
 
57
70
  tzinfo = self.target.datetime.tzinfo
58
71
 
59
- var_log = self.target.fs.path("/var/log")
60
- for log_file in chain(var_log.glob("syslog*"), var_log.glob("messages*")):
72
+ for log_file in self.log_files:
73
+ if "cloud-init" in log_file.name:
74
+ yield from self._parse_cloud_init_log(log_file)
75
+ continue
76
+
61
77
  for ts, line in year_rollover_helper(log_file, RE_TS, DEFAULT_TS_LOG_FORMAT, tzinfo):
62
78
  daemon = dict(enumerate(RE_DAEMON.findall(line))).get(0)
63
79
  pid = dict(enumerate(RE_PID.findall(line))).get(0)
@@ -71,3 +87,32 @@ class MessagesPlugin(Plugin):
71
87
  source=log_file,
72
88
  _target=self.target,
73
89
  )
90
+
91
+ def _parse_cloud_init_log(self, log_file: Path) -> Iterator[MessagesRecord]:
92
+ """Parse a cloud-init.log file.
93
+
94
+ Lines are structured in the following format:
95
+ ``YYYY-MM-DD HH:MM:SS,000 - dhcp.py[DEBUG]: Received dhcp lease on IFACE for IP/MASK``
96
+
97
+ NOTE: ``cloud-init-output.log`` files are not supported as they do not contain structured logs.
98
+
99
+ Args:
100
+ ``log_file``: path to cloud-init.log file.
101
+
102
+ Returns: ``MessagesRecord``
103
+ """
104
+ for line in log_file.open("rt").readlines():
105
+ if line := line.strip():
106
+ if match := RE_CLOUD_INIT_LINE.match(line):
107
+ match = match.groupdict()
108
+ yield MessagesRecord(
109
+ ts=match["ts"].split(",")[0],
110
+ daemon=match["daemon"],
111
+ pid=None,
112
+ message=match["message"],
113
+ source=log_file,
114
+ _target=self.target,
115
+ )
116
+ else:
117
+ self.target.log.warning("Could not match cloud-init log line")
118
+ self.target.log.debug("No match for line '%s'", line)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.17.dev25
3
+ Version: 3.17.dev27
4
4
  Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -57,7 +57,7 @@ dissect/target/helpers/loaderutil.py,sha256=kiyMWra_gVxfNSGwLlgxLcuuqAYuCMDc5NiC
57
57
  dissect/target/helpers/localeutil.py,sha256=Y4Fh4jDSGfm5356xSLMriUCN8SZP_FAHg_iodkAxNq4,1504
58
58
  dissect/target/helpers/mount.py,sha256=JxhUYyEbDnHfzPpfuWy4nV9OwCJPoDSGdHHNiyvd_l0,3949
59
59
  dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q,22258
60
- dissect/target/helpers/network_managers.py,sha256=OgrYhbBqM6K5OfUnCdTLG0RBrR-DcpR1CPezbNddK7k,24667
60
+ dissect/target/helpers/network_managers.py,sha256=uRh_P8ICbKke2N7eFJ6AS2-I5DmIRiaQUlxR7oqxPaU,24975
61
61
  dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
62
62
  dissect/target/helpers/protobuf.py,sha256=NwKrZD4q9v7J8GnZX9gbzMUMV5pR78eAV17jgWOz_EY,1730
63
63
  dissect/target/helpers/record.py,sha256=lWl7k2Mp9Axllm0tXzPGJx2zj2zONsyY_p5g424T0Lc,4826
@@ -133,10 +133,10 @@ dissect/target/plugins/apps/remoteaccess/teamviewer.py,sha256=SiEH36HM2NvdPuCjfL
133
133
  dissect/target/plugins/apps/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
134
  dissect/target/plugins/apps/shell/powershell.py,sha256=biPSMRWxPI6kRqP0-75yMtrw0Ti2Bzfl_xI3xbmmF48,2641
135
135
  dissect/target/plugins/apps/ssh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
136
- dissect/target/plugins/apps/ssh/openssh.py,sha256=jDNP8aq9JHivosexPlxWRUgeJo1MHclb336dzO1zRJc,7086
136
+ dissect/target/plugins/apps/ssh/openssh.py,sha256=yt3bX93Q9wfF25_vG9APMwfZWUUqCPyLlVJdhu20syI,7250
137
137
  dissect/target/plugins/apps/ssh/opensshd.py,sha256=DaXKdgGF3GYHHA4buEvphcm6FF4C8YFjgD96Dv6rRnM,5510
138
- dissect/target/plugins/apps/ssh/putty.py,sha256=N8ssjutUVN50JNA5fEIVISbP5sJ7bGTFidRbX3uNG5Y,9404
139
- dissect/target/plugins/apps/ssh/ssh.py,sha256=uCaoWlT2bgKLUHA1aL6XymJDWJ8JmLsN8PB1C66eidY,1409
138
+ dissect/target/plugins/apps/ssh/putty.py,sha256=bTX6ZU6zsc78zBdsBGUotc_ek_E1a7HSowoqD1y4PzA,9927
139
+ dissect/target/plugins/apps/ssh/ssh.py,sha256=tTA87u0B8yY1yVCPV0VJdRUct6ggkir_pIziP-eKnVo,3009
140
140
  dissect/target/plugins/apps/vpn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
141
  dissect/target/plugins/apps/vpn/openvpn.py,sha256=d-DGINTIHP_bvv3T09ZwbezHXGctvCyAhJ482m2_-a0,7654
142
142
  dissect/target/plugins/apps/vpn/wireguard.py,sha256=SoAMED_bwWJQ3nci5qEY-qV4wJKSSDZQ8K7DoJRYq0k,6521
@@ -247,7 +247,7 @@ dissect/target/plugins/os/unix/log/audit.py,sha256=OjorWTmCFvCI5RJq6m6WNW0Lhb-po
247
247
  dissect/target/plugins/os/unix/log/auth.py,sha256=l7gCuRdvv9gL0U1N0yrR9hVsMnr4t_k4t-n-f6PrOxg,2388
248
248
  dissect/target/plugins/os/unix/log/journal.py,sha256=eiNNVLmKWFj4dTQX8PNRNgKpVwzQWEHEsKyYfGUAPXQ,17376
249
249
  dissect/target/plugins/os/unix/log/lastlog.py,sha256=eL_dbB1sPoy0tyavIjT457ZLVfXcCr17GiwDrMEEh8A,2458
250
- dissect/target/plugins/os/unix/log/messages.py,sha256=W3CeI0tchdRql9SKLFDxk9AKwUvqIrnpCujcERvDt90,2846
250
+ dissect/target/plugins/os/unix/log/messages.py,sha256=CXA-SkMPLaCgnTQg9nzII-7tO8Il_ENQmuYvDxo33rI,4698
251
251
  dissect/target/plugins/os/unix/log/utmp.py,sha256=21tvzG977LqzRShV6uAoU-83WDcLUrI_Tv__2ZVi9rw,7756
252
252
  dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
253
  dissect/target/plugins/os/windows/_os.py,sha256=EA9B9Rgb1C9NMvlX3gXhTRFXYaI6zrrKRg0OYq4v1ts,12589
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
336
336
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
337
337
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
338
338
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
339
- dissect.target-3.17.dev25.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
- dissect.target-3.17.dev25.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
- dissect.target-3.17.dev25.dist-info/METADATA,sha256=9gF0AAh1PZsU75RQtErozfTPL0q2jyWQo1i76L34lR0,11300
342
- dissect.target-3.17.dev25.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
- dissect.target-3.17.dev25.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
- dissect.target-3.17.dev25.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
- dissect.target-3.17.dev25.dist-info/RECORD,,
339
+ dissect.target-3.17.dev27.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
+ dissect.target-3.17.dev27.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
+ dissect.target-3.17.dev27.dist-info/METADATA,sha256=3-kTMZehcHT31jjm50J9_Msj1Pw6LqWUMsiMaSaLiBY,11300
342
+ dissect.target-3.17.dev27.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
+ dissect.target-3.17.dev27.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
+ dissect.target-3.17.dev27.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
+ dissect.target-3.17.dev27.dist-info/RECORD,,