dissect.target 3.11.dev36__py3-none-any.whl → 3.11.2.dev1__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.
dissect/target/plugin.py CHANGED
@@ -63,6 +63,7 @@ class OperatingSystem(enum.Enum):
63
63
  VYOS = "vyos"
64
64
  IOS = "ios"
65
65
  FORTIGATE = "fortigate"
66
+ CITRIX = "citrix-netscaler"
66
67
 
67
68
 
68
69
  def export(*args, **kwargs) -> Callable:
File without changes
@@ -0,0 +1,72 @@
1
+ import re
2
+ from datetime import datetime
3
+ from typing import Iterator
4
+
5
+ from dissect.target.exceptions import UnsupportedPluginError
6
+ from dissect.target.helpers.record import TargetRecordDescriptor
7
+ from dissect.target.plugin import Plugin, export
8
+
9
+ CPanelLastloginRecord = TargetRecordDescriptor(
10
+ "application/log/cpanel/lastlogin",
11
+ [
12
+ ("datetime", "ts"),
13
+ ("string", "user"),
14
+ ("net.ipaddress", "remote_ip"),
15
+ ],
16
+ )
17
+
18
+ CPANEL_LASTLOGIN = ".lastlogin"
19
+ CPANEL_LOGS_PATH = "/usr/local/cpanel/logs"
20
+ CPANEL_LASTLOGIN_PATTERN = re.compile(
21
+ r"([^\s]+) # ([0-9]{4}-[0-9]{2}-[0-9]{2}) ([0-9]{2}:[0-9]{2}:[0-9]{2}) ([+-][0-9]{4})"
22
+ )
23
+
24
+
25
+ class CPanelPlugin(Plugin):
26
+ # TODO: Parse other log files https://support.cartika.com/portal/en/kb/articles/whm-cpanel-log-files-and-locations
27
+ __namespace__ = "cpanel"
28
+
29
+ def check_compatible(self) -> None:
30
+ if not self.target.fs.path(CPANEL_LOGS_PATH).exists():
31
+ raise UnsupportedPluginError("No cPanel log path found")
32
+
33
+ @export(record=CPanelLastloginRecord)
34
+ def lastlogin(self) -> Iterator[CPanelLastloginRecord]:
35
+ """Return the content of the cPanel lastlogin file.
36
+
37
+ The lastlogin files tracks successful cPanel interface logons. New logon events are only tracked
38
+ if the IP-address of the logon changes.
39
+
40
+ References:
41
+ - https://forums.cpanel.net/threads/cpanel-control-panel-last-login-clarification.579221/
42
+ - https://forums.cpanel.net/threads/lastlogin.707557/
43
+ """
44
+ for user_details in self.target.user_details.all_with_home():
45
+ if (lastlogin := user_details.home_path.joinpath(CPANEL_LASTLOGIN)).exists():
46
+ try:
47
+ for index, line in enumerate(lastlogin.open("rt")):
48
+ line = line.strip()
49
+ if not line:
50
+ continue
51
+
52
+ if events := CPANEL_LASTLOGIN_PATTERN.findall(line):
53
+ for event in events:
54
+ remote_ip, date, time, utc_offset = event
55
+
56
+ timestamp = datetime.strptime(f"{date} {time} {utc_offset}", "%Y-%m-%d %H:%M:%S %z")
57
+
58
+ yield CPanelLastloginRecord(
59
+ ts=timestamp,
60
+ user=user_details.user.name,
61
+ remote_ip=remote_ip,
62
+ _target=self.target,
63
+ )
64
+ else:
65
+ self.target.log.warning(
66
+ "The cPanel lastlogin line number %s is malformed: %s", index + 1, lastlogin
67
+ )
68
+
69
+ except Exception:
70
+ self.target.log.warning(
71
+ "An error occurred parsing cPanel lastlogin line number %i in file: %s", index + 1, lastlogin
72
+ )
File without changes
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Iterator, Optional
5
+
6
+ from dissect.target.filesystem import Filesystem, VirtualFilesystem
7
+ from dissect.target.helpers.record import UnixUserRecord
8
+ from dissect.target.plugin import OperatingSystem, export
9
+ from dissect.target.plugins.os.unix.bsd._os import BsdPlugin
10
+ from dissect.target.target import Target
11
+
12
+ RE_CONFIG_IP = re.compile(r"-IPAddress (?P<ip>[^ ]+) ")
13
+ RE_CONFIG_HOSTNAME = re.compile(r"set ns hostName (?P<hostname>[^\n]+)\n")
14
+ RE_CONFIG_TIMEZONE = re.compile(
15
+ r'set ns param -timezone "GMT\+(?P<hours>[0-9]+):(?P<minutes>[0-9]+)-.*-(?P<zone_name>.+)"'
16
+ )
17
+ RE_CONFIG_USER = re.compile(r"bind system user (?P<user>[^ ]+) ")
18
+ RE_LOADER_CONFIG_KERNEL_VERSION = re.compile(r'kernel="/(?P<version>.*)"')
19
+
20
+
21
+ class CitrixBsdPlugin(BsdPlugin):
22
+ def __init__(self, target: Target):
23
+ super().__init__(target)
24
+ self._ips = []
25
+ self._hostname = None
26
+ self.config_usernames = []
27
+ self._parse_netscaler_configs()
28
+
29
+ def _parse_netscaler_configs(self) -> None:
30
+ ips = set()
31
+ usernames = set()
32
+ for config_path in self.target.fs.path("/flash/nsconfig/").glob("ns.conf*"):
33
+ with config_path.open("rt") as config_file:
34
+ config = config_file.read()
35
+ for match in RE_CONFIG_IP.finditer(config):
36
+ ips.add(match.groupdict()["ip"])
37
+ for match in RE_CONFIG_USER.finditer(config):
38
+ usernames.add(match.groupdict()["user"])
39
+ if config_path.name == "ns.conf":
40
+ # Current configuration of the netscaler
41
+ if hostname_match := RE_CONFIG_HOSTNAME.search(config):
42
+ self._hostname = hostname_match.groupdict()["hostname"]
43
+ if timezone_match := RE_CONFIG_TIMEZONE.search(config):
44
+ tzinfo = timezone_match.groupdict()
45
+ self.target.timezone = tzinfo["zone_name"]
46
+
47
+ self._config_usernames = list(usernames)
48
+ self._ips = list(ips)
49
+
50
+ @classmethod
51
+ def detect(cls, target: Target) -> Optional[Filesystem]:
52
+ newfilesystem = VirtualFilesystem()
53
+ is_citrix = False
54
+ for fs in target.filesystems:
55
+ if fs.exists("/bin/freebsd-version"):
56
+ newfilesystem.map_fs("/", fs)
57
+ break
58
+ for fs in target.filesystems:
59
+ if fs.exists("/nsconfig") and fs.exists("/boot"):
60
+ newfilesystem.map_fs("/flash", fs)
61
+ is_citrix = True
62
+ elif fs.exists("/netscaler"):
63
+ newfilesystem.map_fs("/var", fs)
64
+ is_citrix = True
65
+ if is_citrix:
66
+ return newfilesystem
67
+ return None
68
+
69
+ @export(property=True)
70
+ def hostname(self) -> Optional[str]:
71
+ return self._hostname
72
+
73
+ @export(property=True)
74
+ def version(self) -> Optional[str]:
75
+ version_path = self.target.fs.path("/flash/.version")
76
+ version = version_path.read_text().strip()
77
+ loader_conf = self.target.fs.path("/flash/boot/loader.conf").read_text()
78
+ if match := RE_LOADER_CONFIG_KERNEL_VERSION.search(loader_conf):
79
+ kernel_version = match.groupdict()["version"]
80
+ return f"{version} ({kernel_version})"
81
+ self.target.log.warn("Could not determine kernel version")
82
+ return version
83
+
84
+ @export(property=True)
85
+ def ips(self) -> list[str]:
86
+ return self._ips
87
+
88
+ @export(record=UnixUserRecord)
89
+ def users(self) -> Iterator[UnixUserRecord]:
90
+ nstmp_users = set()
91
+ nstmp_path = "/var/nstmp/"
92
+
93
+ nstmp_user_path = nstmp_path + "{username}"
94
+
95
+ for entry in self.target.fs.scandir(nstmp_path):
96
+ if entry.is_dir() and entry.name != "#nsinternal#":
97
+ nstmp_users.add(entry.name)
98
+ for username in self._config_usernames:
99
+ nstmp_home = nstmp_user_path.format(username=username)
100
+ user_home = nstmp_home if self.target.fs.exists(nstmp_home) else None
101
+
102
+ if user_home:
103
+ # After this loop we will yield all users who are not in the config, but are listed in /var/nstmp/
104
+ # To prevent double records, we remove entries from the set that we are already yielding here.
105
+ nstmp_users.remove(username)
106
+
107
+ if username == "root" and self.target.fs.exists("/root"):
108
+ # If we got here, 'root' is present both in /var/nstmp and in /root. In such cases, we yield
109
+ # the 'root' user as having '/root' as a home, not in /var/nstmp.
110
+ user_home = "/root"
111
+
112
+ yield UnixUserRecord(name=username, home=user_home)
113
+
114
+ for username in nstmp_users:
115
+ yield UnixUserRecord(name=username, home=nstmp_user_path.format(username=username))
116
+
117
+ @export(property=True)
118
+ def os(self) -> str:
119
+ return OperatingSystem.CITRIX.value
@@ -210,7 +210,7 @@ class RegistryPlugin(Plugin):
210
210
  Returns a KeyCollection which contains all keys that match
211
211
  the query.
212
212
  """
213
- key = key.strip("\\")
213
+ key = (key or "").strip("\\")
214
214
 
215
215
  if not key:
216
216
  return KeyCollection([self._root.root()])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.11.dev36
3
+ Version: 3.11.2.dev1
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
@@ -3,7 +3,7 @@ dissect/target/container.py,sha256=R8M9EE7DqKq8DeMuekcpR1nxtZ827zuqmTmO4s7PYkg,7
3
3
  dissect/target/exceptions.py,sha256=DQVgo6puVBRPBiappL9GU5EA94lrcr3eVta0m56a-ng,2777
4
4
  dissect/target/filesystem.py,sha256=Kn9RJtdYUWRXh4hxGnHpN_ttwcslZpwzVtUvX_W7qIQ,49335
5
5
  dissect/target/loader.py,sha256=oTpNhmb2abgWuPUqdewLjZz2zcbSYQP5kzZ5yYu7XXg,7100
6
- dissect/target/plugin.py,sha256=qHFKipJP8sBQbn1HOSJna25Fspt7PZ-i5RoVsdY1cGM,32565
6
+ dissect/target/plugin.py,sha256=fkM_Qn5M0R8DQ7JMeVTHH8h1JbHQ2HDFHjqdUKXHFRc,32597
7
7
  dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
8
8
  dissect/target/target.py,sha256=TgDY-yAsReOQOG-Phz_m1vdNucdbk9fUI_RMZpMeYG8,28334
9
9
  dissect/target/volume.py,sha256=vHBXdDttpiu-Q_oWycNM7fdJ5N8Ob7-i_UBJK9DEs24,15027
@@ -103,6 +103,8 @@ dissect/target/plugins/apps/ssh/openssh.py,sha256=bOtUj_jrie8ncTmtj_cCmFh169i4eW
103
103
  dissect/target/plugins/apps/vpns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
104
  dissect/target/plugins/apps/vpns/openvpn.py,sha256=OP2dUIlAdVpMAW9OQycWQpigmemF1AJKIEUVNgTB7HQ,6622
105
105
  dissect/target/plugins/apps/vpns/wireguard.py,sha256=LpGwbABhrViMVUJ-QWS1leLHyjwVtIMIp-dzkvarE0c,5773
106
+ dissect/target/plugins/apps/webhosting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
+ dissect/target/plugins/apps/webhosting/cpanel.py,sha256=OeFQnu9GmpffIlFyK-AR2Qf8tjyMhazWEAUyccDU5y0,2979
106
108
  dissect/target/plugins/apps/webservers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
109
  dissect/target/plugins/apps/webservers/apache.py,sha256=tEH65kRDzNnRkn_oy9pjGqzvP82gx-G4ZRVjyjY_rU4,7091
108
110
  dissect/target/plugins/apps/webservers/caddy.py,sha256=uwrH1pYkKaaa9GGueyCi2QO_4WT0msvm_ju7Ff3YxuU,6306
@@ -157,6 +159,8 @@ dissect/target/plugins/os/unix/packagemanager.py,sha256=-mxNhDjvj497-wBvu3z22316
157
159
  dissect/target/plugins/os/unix/services.py,sha256=OEnaenGORK9K3_HfF8Ii5Pp0RsVYBJObEELY-gsnumE,5293
158
160
  dissect/target/plugins/os/unix/shadow.py,sha256=7ztW_fYLihxNjS2opFToF-xKZngYDGcTEbZKnodRkc8,3409
159
161
  dissect/target/plugins/os/unix/bsd/_os.py,sha256=e5rttTOFOmd7e2HqP9ZZFMEiPLBr-8rfH0XH1IIeroQ,1372
162
+ dissect/target/plugins/os/unix/bsd/citrix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
+ dissect/target/plugins/os/unix/bsd/citrix/_os.py,sha256=BsmrDOu_izsapi-iGUlxQmcJsiBhN9zUcU3np-PrdZc,4939
160
164
  dissect/target/plugins/os/unix/bsd/freebsd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
161
165
  dissect/target/plugins/os/unix/bsd/freebsd/_os.py,sha256=Vqiyn08kv1IioNUwpgtBJ9SToCFhLCsJdpVhl5E7COM,789
162
166
  dissect/target/plugins/os/unix/bsd/ios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -214,7 +218,7 @@ dissect/target/plugins/os/windows/locale.py,sha256=YlRqFteHGSE-A21flbCKP1jXUTgyX
214
218
  dissect/target/plugins/os/windows/notifications.py,sha256=tBgZKnDCXWFtz7chHIo5cKQf2swcTTB3MMcecfTZ-4w,4773
215
219
  dissect/target/plugins/os/windows/prefetch.py,sha256=favUyI5Pywi8Ho8fUye3gnXcM9BqEIMhFcSa1idQQBg,10304
216
220
  dissect/target/plugins/os/windows/recyclebin.py,sha256=aqp1kc8A6k5UTt6ebycuejPd0QJwNIX1xIu21M0CUGU,4926
217
- dissect/target/plugins/os/windows/registry.py,sha256=aRIAq7pXtPpdGBxU_WOpOkceu1GE9N51ssriuuW9wfs,12227
221
+ dissect/target/plugins/os/windows/registry.py,sha256=cLv5T5eomc8APn6eXKIx26Ea3Y4i3eledDFGI0Q-q_U,12235
218
222
  dissect/target/plugins/os/windows/sam.py,sha256=jFl22o5WhjgdD2fWaebhfnLrx1g_T1BYXodVGSIQzRk,15789
219
223
  dissect/target/plugins/os/windows/services.py,sha256=p2v4z4YM-K3G2cnWIHVyPgsJgfrlDpvXz7gUvltIUD4,6059
220
224
  dissect/target/plugins/os/windows/sru.py,sha256=4Vybz3_RJYNbLZXKYGOouUKZNWyOUSgSTf4JAGN2O7w,16808
@@ -275,10 +279,10 @@ dissect/target/volumes/bde.py,sha256=gYGg5yF9MNARwNzEkrEfZmKkxyZW4rhLkpdnPJCbhGk
275
279
  dissect/target/volumes/disk.py,sha256=95grSsPt1BLVpKwTclwQYzPFGKTkFFqapIk0RoGWf38,968
276
280
  dissect/target/volumes/lvm.py,sha256=_kIB1mdRs1OFhRgoT4VEP5Fv8imQnI7oQ_ie4x710tQ,1814
277
281
  dissect/target/volumes/vmfs.py,sha256=mlAJ8278tYaoRjk1u6tFFlCaDQUrVu5ZZE4ikiFvxi8,1707
278
- dissect.target-3.11.dev36.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
279
- dissect.target-3.11.dev36.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
280
- dissect.target-3.11.dev36.dist-info/METADATA,sha256=1tL7rKZLxN4x6D0dr57ST9VZ8NiF2Yuoel5b_tTiyDg,10711
281
- dissect.target-3.11.dev36.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
282
- dissect.target-3.11.dev36.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
283
- dissect.target-3.11.dev36.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
284
- dissect.target-3.11.dev36.dist-info/RECORD,,
282
+ dissect.target-3.11.2.dev1.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
283
+ dissect.target-3.11.2.dev1.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
284
+ dissect.target-3.11.2.dev1.dist-info/METADATA,sha256=Bpf8TdpjIStxlBta0fNZO6ytz6f2caTDAgXftEX4pqw,10712
285
+ dissect.target-3.11.2.dev1.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
286
+ dissect.target-3.11.2.dev1.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
287
+ dissect.target-3.11.2.dev1.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
288
+ dissect.target-3.11.2.dev1.dist-info/RECORD,,