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 +1 -0
- dissect/target/plugins/apps/webhosting/__init__.py +0 -0
- dissect/target/plugins/apps/webhosting/cpanel.py +72 -0
- dissect/target/plugins/os/unix/bsd/citrix/__init__.py +0 -0
- dissect/target/plugins/os/unix/bsd/citrix/_os.py +119 -0
- dissect/target/plugins/os/windows/registry.py +1 -1
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/METADATA +1 -1
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/RECORD +13 -9
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/LICENSE +0 -0
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/WHEEL +0 -0
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/top_level.txt +0 -0
dissect/target/plugin.py
CHANGED
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
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.11.
|
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=
|
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=
|
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.
|
279
|
-
dissect.target-3.11.
|
280
|
-
dissect.target-3.11.
|
281
|
-
dissect.target-3.11.
|
282
|
-
dissect.target-3.11.
|
283
|
-
dissect.target-3.11.
|
284
|
-
dissect.target-3.11.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.11.dev36.dist-info → dissect.target-3.11.2.dev1.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|