dissect.target 3.20.dev36__py3-none-any.whl → 3.20.dev39__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/filesystems/config.py +1 -1
- dissect/target/helpers/compat/path_common.py +5 -5
- dissect/target/helpers/configutil.py +31 -29
- dissect/target/helpers/cyber.py +2 -0
- dissect/target/helpers/docs.py +1 -1
- dissect/target/helpers/keychain.py +2 -0
- dissect/target/helpers/mount.py +2 -1
- dissect/target/plugin.py +15 -13
- dissect/target/plugins/apps/av/mcafee.py +2 -0
- dissect/target/plugins/apps/av/sophos.py +2 -0
- dissect/target/plugins/apps/av/trendmicro.py +2 -0
- dissect/target/plugins/apps/browser/chromium.py +27 -6
- dissect/target/plugins/apps/editor/editor.py +23 -0
- dissect/target/plugins/apps/{texteditor → editor}/windowsnotepad.py +40 -31
- dissect/target/plugins/apps/shell/powershell.py +6 -2
- dissect/target/plugins/apps/shell/wget.py +1 -1
- dissect/target/plugins/apps/ssh/openssh.py +2 -0
- dissect/target/plugins/apps/ssh/opensshd.py +2 -0
- dissect/target/plugins/apps/vpn/wireguard.py +9 -9
- dissect/target/plugins/apps/webhosting/cpanel.py +2 -0
- dissect/target/plugins/apps/webserver/caddy.py +2 -0
- dissect/target/plugins/apps/webserver/nginx.py +2 -0
- dissect/target/plugins/child/esxi.py +3 -1
- dissect/target/plugins/child/virtuozzo.py +12 -8
- dissect/target/plugins/filesystem/acquire_hash.py +2 -1
- dissect/target/plugins/filesystem/icat.py +15 -11
- dissect/target/plugins/filesystem/ntfs/mft.py +2 -0
- dissect/target/plugins/filesystem/ntfs/mft_timeline.py +3 -1
- dissect/target/plugins/filesystem/ntfs/usnjrnl.py +2 -0
- dissect/target/plugins/filesystem/ntfs/utils.py +3 -1
- dissect/target/plugins/filesystem/unix/suid.py +4 -1
- dissect/target/plugins/filesystem/walkfs.py +2 -0
- dissect/target/plugins/general/example.py +2 -2
- dissect/target/plugins/general/loaders.py +1 -1
- dissect/target/plugins/general/network.py +7 -0
- dissect/target/plugins/general/osinfo.py +1 -0
- dissect/target/plugins/general/plugins.py +4 -0
- dissect/target/plugins/os/unix/_os.py +2 -1
- dissect/target/plugins/os/unix/bsd/citrix/history.py +2 -0
- dissect/target/plugins/os/unix/bsd/osx/network.py +2 -0
- dissect/target/plugins/os/unix/bsd/osx/user.py +4 -0
- dissect/target/plugins/os/unix/cronjobs.py +8 -4
- dissect/target/plugins/os/unix/etc/etc.py +4 -0
- dissect/target/plugins/os/unix/generic.py +2 -0
- dissect/target/plugins/os/unix/history.py +27 -25
- dissect/target/plugins/os/unix/linux/cmdline.py +2 -0
- dissect/target/plugins/os/unix/linux/debian/apt.py +4 -1
- dissect/target/plugins/os/unix/linux/debian/dpkg.py +3 -3
- dissect/target/plugins/os/unix/linux/environ.py +2 -0
- dissect/target/plugins/os/unix/linux/fortios/generic.py +2 -0
- dissect/target/plugins/os/unix/linux/fortios/locale.py +2 -0
- dissect/target/plugins/os/unix/linux/modules.py +2 -0
- dissect/target/plugins/os/unix/linux/netstat.py +2 -0
- dissect/target/plugins/os/unix/linux/processes.py +2 -0
- dissect/target/plugins/os/unix/linux/redhat/yum.py +4 -1
- dissect/target/plugins/os/unix/linux/services.py +5 -3
- dissect/target/plugins/os/unix/linux/sockets.py +2 -0
- dissect/target/plugins/os/unix/linux/suse/zypper.py +4 -1
- dissect/target/plugins/os/unix/locale.py +2 -0
- dissect/target/plugins/os/unix/locate/gnulocate.py +4 -2
- dissect/target/plugins/os/unix/locate/mlocate.py +2 -0
- dissect/target/plugins/os/unix/locate/plocate.py +3 -1
- dissect/target/plugins/os/unix/log/atop.py +2 -0
- dissect/target/plugins/os/unix/log/audit.py +3 -1
- dissect/target/plugins/os/unix/log/auth.py +4 -2
- dissect/target/plugins/os/unix/log/lastlog.py +5 -3
- dissect/target/plugins/os/unix/log/messages.py +2 -0
- dissect/target/plugins/os/unix/log/utmp.py +4 -2
- dissect/target/plugins/os/unix/packagemanager.py +4 -37
- dissect/target/plugins/os/unix/shadow.py +3 -1
- dissect/target/plugins/os/unix/trash.py +1 -1
- dissect/target/plugins/os/windows/activitiescache.py +9 -4
- dissect/target/plugins/os/windows/adpolicy.py +2 -1
- dissect/target/plugins/os/windows/amcache.py +16 -13
- dissect/target/plugins/os/windows/defender.py +3 -3
- dissect/target/plugins/os/windows/dpapi/keyprovider/credhist.py +3 -0
- dissect/target/plugins/os/windows/dpapi/keyprovider/empty.py +3 -0
- dissect/target/plugins/os/windows/dpapi/keyprovider/keychain.py +3 -0
- dissect/target/plugins/os/windows/dpapi/keyprovider/lsa.py +3 -0
- dissect/target/plugins/os/windows/env.py +1 -2
- dissect/target/plugins/os/windows/exchange/exchange.py +6 -4
- dissect/target/plugins/os/windows/generic.py +20 -18
- dissect/target/plugins/os/windows/lnk.py +2 -0
- dissect/target/plugins/os/windows/locale.py +9 -3
- dissect/target/plugins/os/windows/log/etl.py +5 -4
- dissect/target/plugins/os/windows/log/evt.py +12 -8
- dissect/target/plugins/os/windows/log/evtx.py +9 -7
- dissect/target/plugins/os/windows/log/pfro.py +2 -1
- dissect/target/plugins/os/windows/network.py +2 -0
- dissect/target/plugins/os/windows/notifications.py +6 -4
- dissect/target/plugins/os/windows/prefetch.py +7 -2
- dissect/target/plugins/os/windows/regf/7zip.py +9 -1
- dissect/target/plugins/os/windows/regf/auditpol.py +2 -1
- dissect/target/plugins/os/windows/regf/bam.py +3 -1
- dissect/target/plugins/os/windows/regf/cit.py +14 -12
- dissect/target/plugins/os/windows/regf/clsid.py +6 -3
- dissect/target/plugins/os/windows/regf/firewall.py +2 -1
- dissect/target/plugins/os/windows/regf/mru.py +9 -8
- dissect/target/plugins/os/windows/regf/nethist.py +6 -3
- dissect/target/plugins/os/windows/regf/recentfilecache.py +3 -1
- dissect/target/plugins/os/windows/regf/regf.py +5 -1
- dissect/target/plugins/os/windows/regf/shimcache.py +1 -1
- dissect/target/plugins/os/windows/regf/usb.py +2 -1
- dissect/target/plugins/os/windows/regf/userassist.py +2 -1
- dissect/target/plugins/os/windows/registry.py +11 -0
- dissect/target/plugins/os/windows/services.py +3 -2
- dissect/target/plugins/os/windows/startupinfo.py +7 -2
- dissect/target/plugins/os/windows/syscache.py +3 -1
- dissect/target/plugins/os/windows/tasks.py +1 -1
- dissect/target/plugins/os/windows/thumbcache.py +11 -5
- dissect/target/plugins/os/windows/ual.py +12 -9
- dissect/target/plugins/os/windows/wua_history.py +0 -1
- dissect/target/tools/dump/utils.py +4 -0
- dissect/target/tools/shell.py +2 -1
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/RECORD +122 -123
- dissect/target/plugins/apps/texteditor/texteditor.py +0 -13
- dissect/target/plugins/os/unix/etc.py +0 -9
- /dissect/target/plugins/apps/{texteditor → editor}/__init__.py +0 -0
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev36.dist-info → dissect.target-3.20.dev39.dist-info}/top_level.txt +0 -0
@@ -7,10 +7,13 @@ from dissect.target.helpers.fsutil import open_decompress
|
|
7
7
|
from dissect.target.plugins.os.unix.packagemanager import (
|
8
8
|
OperationTypes,
|
9
9
|
PackageManagerLogRecord,
|
10
|
+
PackageManagerPlugin,
|
10
11
|
)
|
11
12
|
|
12
13
|
|
13
|
-
class ZypperPlugin(
|
14
|
+
class ZypperPlugin(PackageManagerPlugin):
|
15
|
+
"""Zypper package manager plugin."""
|
16
|
+
|
14
17
|
__namespace__ = "zypper"
|
15
18
|
|
16
19
|
LOG_DIR_PATH = "/var/log/zypp"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import BinaryIO, Iterable
|
3
|
+
from typing import BinaryIO, Iterable, Iterator
|
4
4
|
|
5
5
|
from dissect.cstruct import cstruct
|
6
6
|
|
@@ -69,6 +69,8 @@ class GNULocateFile:
|
|
69
69
|
|
70
70
|
|
71
71
|
class GNULocatePlugin(BaseLocatePlugin):
|
72
|
+
"""GNU locate plugin."""
|
73
|
+
|
72
74
|
__namespace__ = "gnulocate"
|
73
75
|
|
74
76
|
path = "/var/cache/locate/locatedb"
|
@@ -78,7 +80,7 @@ class GNULocatePlugin(BaseLocatePlugin):
|
|
78
80
|
raise UnsupportedPluginError(f"No locatedb file found at {self.path}")
|
79
81
|
|
80
82
|
@export(record=GNULocateRecord)
|
81
|
-
def locate(self) -> GNULocateRecord:
|
83
|
+
def locate(self) -> Iterator[GNULocateRecord]:
|
82
84
|
"""Yield file and directory names from GNU findutils' locatedb file.
|
83
85
|
|
84
86
|
Resources:
|
@@ -163,6 +163,8 @@ class PLocateFile:
|
|
163
163
|
|
164
164
|
|
165
165
|
class PLocatePlugin(BaseLocatePlugin):
|
166
|
+
"""Unix plocate plugin."""
|
167
|
+
|
166
168
|
__namespace__ = "plocate"
|
167
169
|
|
168
170
|
path = "/var/lib/plocate/plocate.db"
|
@@ -177,7 +179,7 @@ class PLocatePlugin(BaseLocatePlugin):
|
|
177
179
|
)
|
178
180
|
|
179
181
|
@export(record=PLocateRecord)
|
180
|
-
def locate(self) -> PLocateRecord:
|
182
|
+
def locate(self) -> Iterator[PLocateRecord]:
|
181
183
|
"""Yield file and directory names from the plocate.db.
|
182
184
|
|
183
185
|
``plocate`` is the default package on Ubuntu 22 and newer to locate files.
|
@@ -24,6 +24,8 @@ AUDIT_REGEX = re.compile(r"^type=(?P<audit_type>.*) msg=audit\((?P<ts>.*):(?P<au
|
|
24
24
|
|
25
25
|
|
26
26
|
class AuditPlugin(Plugin):
|
27
|
+
"""Unix audit log plugin."""
|
28
|
+
|
27
29
|
def __init__(self, target):
|
28
30
|
super().__init__(target)
|
29
31
|
self.log_paths = self.get_log_paths()
|
@@ -51,7 +53,7 @@ class AuditPlugin(Plugin):
|
|
51
53
|
|
52
54
|
return log_paths
|
53
55
|
|
54
|
-
@export(record=
|
56
|
+
@export(record=AuditRecord)
|
55
57
|
def audit(self) -> Iterator[AuditRecord]:
|
56
58
|
"""Return CentOS and RedHat audit information stored in /var/log/audit*.
|
57
59
|
|
@@ -22,17 +22,19 @@ RE_TS_AND_HOSTNAME = re.compile(_TS_REGEX + r"\s\S+\s")
|
|
22
22
|
|
23
23
|
|
24
24
|
class AuthPlugin(Plugin):
|
25
|
+
"""Unix auth log plugin."""
|
26
|
+
|
25
27
|
def check_compatible(self) -> None:
|
26
28
|
var_log = self.target.fs.path("/var/log")
|
27
29
|
if not any(var_log.glob("auth.log*")) and not any(var_log.glob("secure*")):
|
28
30
|
raise UnsupportedPluginError("No auth log files found")
|
29
31
|
|
30
|
-
@export(record=
|
32
|
+
@export(record=AuthLogRecord)
|
31
33
|
def securelog(self) -> Iterator[AuthLogRecord]:
|
32
34
|
"""Return contents of /var/log/auth.log* and /var/log/secure*."""
|
33
35
|
return self.authlog()
|
34
36
|
|
35
|
-
@export(record=
|
37
|
+
@export(record=AuthLogRecord)
|
36
38
|
def authlog(self) -> Iterator[AuthLogRecord]:
|
37
39
|
"""Return contents of /var/log/auth.log* and /var/log/secure*."""
|
38
40
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import BinaryIO
|
1
|
+
from typing import BinaryIO, Iterator
|
2
2
|
|
3
3
|
from dissect.cstruct import cstruct
|
4
4
|
from dissect.util import ts
|
@@ -52,13 +52,15 @@ class LastLogFile:
|
|
52
52
|
|
53
53
|
|
54
54
|
class LastLogPlugin(Plugin):
|
55
|
+
"""Unix lastlog plugin."""
|
56
|
+
|
55
57
|
def check_compatible(self) -> None:
|
56
58
|
lastlog = self.target.fs.path("/var/log/lastlog")
|
57
59
|
if not lastlog.exists():
|
58
60
|
raise UnsupportedPluginError("No lastlog file found")
|
59
61
|
|
60
|
-
@export(record=
|
61
|
-
def lastlog(self):
|
62
|
+
@export(record=LastLogRecord)
|
63
|
+
def lastlog(self) -> Iterator[LastLogRecord]:
|
62
64
|
"""Return last logins information from /var/log/lastlog.
|
63
65
|
|
64
66
|
The lastlog file contains the most recent logins of all users on a Unix based operating system.
|
@@ -168,6 +168,8 @@ class UtmpFile:
|
|
168
168
|
|
169
169
|
|
170
170
|
class UtmpPlugin(Plugin):
|
171
|
+
"""Unix utmp log plugin."""
|
172
|
+
|
171
173
|
WTMP_GLOB = "/var/log/wtmp*"
|
172
174
|
BTMP_GLOB = "/var/log/btmp*"
|
173
175
|
|
@@ -180,7 +182,7 @@ class UtmpPlugin(Plugin):
|
|
180
182
|
):
|
181
183
|
raise UnsupportedPluginError("No WTMP or BTMP log files found")
|
182
184
|
|
183
|
-
@export(record=
|
185
|
+
@export(record=BtmpRecord)
|
184
186
|
def btmp(self) -> Iterator[BtmpRecord]:
|
185
187
|
"""Return failed login attempts stored in the btmp file.
|
186
188
|
|
@@ -207,7 +209,7 @@ class UtmpPlugin(Plugin):
|
|
207
209
|
_target=self.target,
|
208
210
|
)
|
209
211
|
|
210
|
-
@export(record=
|
212
|
+
@export(record=WtmpRecord)
|
211
213
|
def wtmp(self) -> Iterator[WtmpRecord]:
|
212
214
|
"""Return the content of the wtmp log files.
|
213
215
|
|
@@ -1,12 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from enum import Enum
|
4
|
-
from typing import Iterator
|
5
4
|
|
6
|
-
from dissect.target import Target
|
7
|
-
from dissect.target.exceptions import UnsupportedPluginError
|
8
5
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
9
|
-
from dissect.target.plugin import
|
6
|
+
from dissect.target.plugin import NamespacePlugin
|
10
7
|
|
11
8
|
PackageManagerLogRecord = TargetRecordDescriptor(
|
12
9
|
"unix/log/packagemanager",
|
@@ -22,6 +19,8 @@ PackageManagerLogRecord = TargetRecordDescriptor(
|
|
22
19
|
|
23
20
|
|
24
21
|
class OperationTypes(Enum):
|
22
|
+
"""Valid operation types."""
|
23
|
+
|
25
24
|
Install = "install"
|
26
25
|
Update = "update"
|
27
26
|
Downgrade = "downgrade"
|
@@ -45,37 +44,5 @@ class OperationTypes(Enum):
|
|
45
44
|
return OperationTypes.Other
|
46
45
|
|
47
46
|
|
48
|
-
class PackageManagerPlugin(
|
47
|
+
class PackageManagerPlugin(NamespacePlugin):
|
49
48
|
__namespace__ = "packagemanager"
|
50
|
-
__findable__ = False
|
51
|
-
|
52
|
-
TOOLS = [
|
53
|
-
"apt",
|
54
|
-
"yum",
|
55
|
-
"zypper",
|
56
|
-
]
|
57
|
-
|
58
|
-
def __init__(self, target: Target):
|
59
|
-
super().__init__(target)
|
60
|
-
self._plugins = []
|
61
|
-
for entry in self.TOOLS:
|
62
|
-
try:
|
63
|
-
self._plugins.append(getattr(self.target, entry))
|
64
|
-
except Exception:
|
65
|
-
target.log.exception(f"Failed to load tool plugin: {entry}")
|
66
|
-
|
67
|
-
def check_compatible(self) -> None:
|
68
|
-
if not len(self._plugins):
|
69
|
-
raise UnsupportedPluginError("No compatible plugins found")
|
70
|
-
|
71
|
-
def _func(self, f: str) -> Iterator[PackageManagerLogRecord]:
|
72
|
-
for p in self._plugins:
|
73
|
-
try:
|
74
|
-
yield from getattr(p, f)()
|
75
|
-
except Exception:
|
76
|
-
self.target.log.exception("Failed to execute package manager plugin: %s.%s", p._name, f)
|
77
|
-
|
78
|
-
@export(record=PackageManagerLogRecord)
|
79
|
-
def logs(self) -> Iterator[PackageManagerLogRecord]:
|
80
|
-
"""Returns logs from all available Unix package managers."""
|
81
|
-
yield from self._func("logs")
|
@@ -25,6 +25,8 @@ UnixShadowRecord = TargetRecordDescriptor(
|
|
25
25
|
|
26
26
|
|
27
27
|
class ShadowPlugin(Plugin):
|
28
|
+
"""Unix shadow passwords plugin."""
|
29
|
+
|
28
30
|
def check_compatible(self) -> None:
|
29
31
|
if not self.target.fs.path("/etc/shadow").exists():
|
30
32
|
raise UnsupportedPluginError("No shadow file found")
|
@@ -36,7 +38,7 @@ class ShadowPlugin(Plugin):
|
|
36
38
|
"""Yield shadow records from /etc/shadow files.
|
37
39
|
|
38
40
|
Resources:
|
39
|
-
- https://manpages.ubuntu.com/manpages/oracular/en/man5/passwd.5.html
|
41
|
+
- https://manpages.ubuntu.com/manpages/oracular/en/man5/passwd.5.html
|
40
42
|
"""
|
41
43
|
|
42
44
|
seen_hashes = set()
|
@@ -59,7 +59,7 @@ class GnomeTrashPlugin(Plugin):
|
|
59
59
|
- https://github.com/GNOME/glib/blob/main/gio/glocalfile.c
|
60
60
|
- https://specifications.freedesktop.org/basedir-spec/latest/
|
61
61
|
|
62
|
-
Yields ``TrashRecord``
|
62
|
+
Yields ``TrashRecord`` records with the following fields:
|
63
63
|
|
64
64
|
.. code-block:: text
|
65
65
|
|
@@ -1,3 +1,8 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from datetime import datetime
|
4
|
+
from typing import Iterator
|
5
|
+
|
1
6
|
from dissect.sql import sqlite3
|
2
7
|
from dissect.util.ts import from_unix
|
3
8
|
|
@@ -42,8 +47,8 @@ class ActivitiesCachePlugin(Plugin):
|
|
42
47
|
"""Plugin that parses the ActivitiesCache.db on newer Windows 10 machines.
|
43
48
|
|
44
49
|
References:
|
45
|
-
https://www.cclsolutionsgroup.com/resources/technical-papers
|
46
|
-
https://salt4n6.com/2018/05/03/windows-10-timeline-forensic-artefacts/
|
50
|
+
- https://www.cclsolutionsgroup.com/resources/technical-papers
|
51
|
+
- https://salt4n6.com/2018/05/03/windows-10-timeline-forensic-artefacts/
|
47
52
|
"""
|
48
53
|
|
49
54
|
def __init__(self, target):
|
@@ -62,7 +67,7 @@ class ActivitiesCachePlugin(Plugin):
|
|
62
67
|
raise UnsupportedPluginError("No ActiviesCache.db files found")
|
63
68
|
|
64
69
|
@export(record=ActivitiesCacheRecord)
|
65
|
-
def activitiescache(self):
|
70
|
+
def activitiescache(self) -> Iterator[ActivitiesCacheRecord]:
|
66
71
|
"""Return ActivitiesCache.db database content.
|
67
72
|
|
68
73
|
The Windows Activities Cache database keeps track of activity on a device, such as application and services
|
@@ -143,7 +148,7 @@ class ActivitiesCachePlugin(Plugin):
|
|
143
148
|
)
|
144
149
|
|
145
150
|
|
146
|
-
def mkts(ts):
|
151
|
+
def mkts(ts: int) -> datetime | None:
|
147
152
|
"""Timestamps inside ActivitiesCache.db are stored in a Unix-like format.
|
148
153
|
|
149
154
|
Source: https://salt4n6.com/2018/05/03/windows-10-timeline-forensic-artefacts/
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from struct import unpack
|
2
|
+
from typing import Iterator
|
2
3
|
|
3
4
|
from defusedxml import ElementTree
|
4
5
|
from dissect.cstruct import cstruct
|
@@ -90,7 +91,7 @@ class ADPolicyPlugin(Plugin):
|
|
90
91
|
self.target.log.warning("Unable to read XML policy file: %s", error)
|
91
92
|
|
92
93
|
@export(record=ADPolicyRecord)
|
93
|
-
def adpolicy(self):
|
94
|
+
def adpolicy(self) -> Iterator[ADPolicyRecord]:
|
94
95
|
"""Return all AD policies (also known as GPOs or Group Policy Objects).
|
95
96
|
|
96
97
|
An Active Directory (AD) maintains global policies that should be adhered by all systems in the domain.
|
@@ -1,4 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
from datetime import datetime, timezone
|
4
|
+
from typing import Iterator
|
2
5
|
|
3
6
|
from dissect.util.ts import wintimestamp
|
4
7
|
|
@@ -292,13 +295,13 @@ class AmcachePluginOldMixin:
|
|
292
295
|
)
|
293
296
|
|
294
297
|
@export(record=ProgramsAppcompatRecord)
|
295
|
-
def programs(self):
|
298
|
+
def programs(self) -> Iterator[ProgramsAppcompatRecord]:
|
296
299
|
"""Return Programs records from Amcache hive."""
|
297
300
|
if self.amcache:
|
298
301
|
yield from self.parse_programs()
|
299
302
|
|
300
303
|
@export(record=FileAppcompatRecord)
|
301
|
-
def files(self):
|
304
|
+
def files(self) -> Iterator[FileAppcompatRecord]:
|
302
305
|
"""Return File records from Amcache hive."""
|
303
306
|
if self.amcache:
|
304
307
|
yield from self.parse_file()
|
@@ -321,9 +324,9 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
321
324
|
* InventoryApplicationShortcut
|
322
325
|
|
323
326
|
References:
|
324
|
-
https://binaryforay.blogspot.com/2015/04/appcompatcache-changes-in-windows-10.html
|
325
|
-
https://
|
326
|
-
https://aboutdfir.com/new-windows-11-pro-22h2-evidence-of-execution-artifact/
|
327
|
+
- https://binaryforay.blogspot.com/2015/04/appcompatcache-changes-in-windows-10.html
|
328
|
+
- https://cyber.gouv.fr/sites/default/files/2019/01/anssi-coriin_2019-analysis_amcache.pdf
|
329
|
+
- https://aboutdfir.com/new-windows-11-pro-22h2-evidence-of-execution-artifact/
|
327
330
|
|
328
331
|
"""
|
329
332
|
|
@@ -545,7 +548,7 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
545
548
|
)
|
546
549
|
|
547
550
|
@export(record=ApplicationAppcompatRecord)
|
548
|
-
def applications(self):
|
551
|
+
def applications(self) -> Iterator[ApplicationAppcompatRecord]:
|
549
552
|
"""Return InventoryApplication records from Amcache hive.
|
550
553
|
|
551
554
|
Amcache is a registry hive that stores information about executed programs. The InventoryApplication key holds
|
@@ -559,7 +562,7 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
559
562
|
yield from self.parse_inventory_application()
|
560
563
|
|
561
564
|
@export(record=ApplicationFileAppcompatRecord)
|
562
|
-
def application_files(self):
|
565
|
+
def application_files(self) -> Iterator[ApplicationFileAppcompatRecord]:
|
563
566
|
"""Return InventoryApplicationFile records from Amcache hive.
|
564
567
|
|
565
568
|
Amcache is a registry hive that stores information about executed programs. The InventoryApplicationFile key
|
@@ -573,7 +576,7 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
573
576
|
yield from self.parse_inventory_application_file()
|
574
577
|
|
575
578
|
@export(record=BinaryAppcompatRecord)
|
576
|
-
def drivers(self):
|
579
|
+
def drivers(self) -> Iterator[BinaryAppcompatRecord]:
|
577
580
|
"""Return InventoryDriverBinary records from Amcache hive.
|
578
581
|
|
579
582
|
Amcache is a registry hive that stores information about executed programs. The InventoryDriverBinary key holds
|
@@ -588,7 +591,7 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
588
591
|
yield from self.parse_inventory_driver_binary()
|
589
592
|
|
590
593
|
@export(record=ShortcutAppcompatRecord)
|
591
|
-
def shortcuts(self):
|
594
|
+
def shortcuts(self) -> Iterator[ShortcutAppcompatRecord]:
|
592
595
|
"""Return InventoryApplicationShortcut records from Amcache hive.
|
593
596
|
|
594
597
|
Amcache is a registry hive that stores information about executed programs. The InventoryApplicationShortcut
|
@@ -604,7 +607,7 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
604
607
|
yield from self.parse_inventory_application_shortcut()
|
605
608
|
|
606
609
|
@export(record=ContainerAppcompatRecord)
|
607
|
-
def device_containers(self):
|
610
|
+
def device_containers(self) -> Iterator[ContainerAppcompatRecord]:
|
608
611
|
"""Return InventoryDeviceContainer records from Amcache hive.
|
609
612
|
|
610
613
|
Amcache is a registry hive that stores information about executed programs. The InventoryDeviceContainer key
|
@@ -619,7 +622,7 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
619
622
|
yield from self.parse_inventory_device_container()
|
620
623
|
|
621
624
|
@export(record=AppLaunchAppcompatRecord)
|
622
|
-
def applaunches(self):
|
625
|
+
def applaunches(self) -> Iterator[AppLaunchAppcompatRecord]:
|
623
626
|
"""Return AppLaunchAppcompatRecord records from Amcache applaunch files (Windows 11 22H2 or later).
|
624
627
|
|
625
628
|
TODO: Research C:\\Windows\\appcompat\\pca\\PcaGeneralDb0.txt and
|
@@ -641,11 +644,11 @@ class AmcachePlugin(AmcachePluginOldMixin, Plugin):
|
|
641
644
|
)
|
642
645
|
|
643
646
|
|
644
|
-
def parse_win_datetime(value: str):
|
647
|
+
def parse_win_datetime(value: str) -> datetime | None:
|
645
648
|
if value:
|
646
649
|
return datetime.strptime(value, "%m/%d/%Y %H:%M:%S")
|
647
650
|
|
648
651
|
|
649
|
-
def parse_win_timestamp(value: str):
|
652
|
+
def parse_win_timestamp(value: str) -> datetime | None:
|
650
653
|
if value:
|
651
654
|
return wintimestamp(value)
|
@@ -4,7 +4,7 @@ import re
|
|
4
4
|
from datetime import datetime, timezone
|
5
5
|
from io import BytesIO
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Any, BinaryIO,
|
7
|
+
from typing import Any, BinaryIO, Iterable, Iterator, TextIO
|
8
8
|
|
9
9
|
import dissect.util.ts as ts
|
10
10
|
from dissect.cstruct import cstruct
|
@@ -434,7 +434,7 @@ class MicrosoftDefenderPlugin(plugin.Plugin):
|
|
434
434
|
raise UnsupportedPluginError("No Defender objects found")
|
435
435
|
|
436
436
|
@plugin.export(record=DefenderLogRecord)
|
437
|
-
def evtx(self) ->
|
437
|
+
def evtx(self) -> Iterator[DefenderLogRecord]:
|
438
438
|
"""Parse Microsoft Defender evtx log files"""
|
439
439
|
|
440
440
|
defender_evtx_field_names = [field_name for _, field_name in DEFENDER_EVTX_FIELDS]
|
@@ -458,7 +458,7 @@ class MicrosoftDefenderPlugin(plugin.Plugin):
|
|
458
458
|
yield DefenderLogRecord(**record_fields, _target=self.target)
|
459
459
|
|
460
460
|
@plugin.export(record=[DefenderQuarantineRecord, DefenderFileQuarantineRecord])
|
461
|
-
def quarantine(self) -> Iterator[
|
461
|
+
def quarantine(self) -> Iterator[DefenderQuarantineRecord | DefenderFileQuarantineRecord]:
|
462
462
|
"""Parse the quarantine folder of Microsoft Defender for quarantine entry resources.
|
463
463
|
|
464
464
|
Quarantine entry resources contain metadata about detected threats that Microsoft Defender has placed in
|
@@ -8,6 +8,8 @@ from dissect.target.plugins.os.windows.dpapi.keyprovider.keyprovider import (
|
|
8
8
|
|
9
9
|
|
10
10
|
class CredHistKeyProviderPlugin(KeyProviderPlugin):
|
11
|
+
"""Windows CREDHIST SHA1-hash key provider plugin."""
|
12
|
+
|
11
13
|
__namespace__ = "_dpapi_keyprovider_credhist"
|
12
14
|
|
13
15
|
def check_compatible(self) -> None:
|
@@ -16,6 +18,7 @@ class CredHistKeyProviderPlugin(KeyProviderPlugin):
|
|
16
18
|
|
17
19
|
@export(output="yield")
|
18
20
|
def keys(self) -> Iterator[tuple[str, str]]:
|
21
|
+
"""Yield Windows CREDHIST SHA1 hashes."""
|
19
22
|
for credhist in self.target.credhist():
|
20
23
|
if value := getattr(credhist, "sha1"):
|
21
24
|
yield self.__namespace__, value
|
@@ -7,6 +7,8 @@ from dissect.target.plugins.os.windows.dpapi.keyprovider.keyprovider import (
|
|
7
7
|
|
8
8
|
|
9
9
|
class EmptyKeyProviderPlugin(KeyProviderPlugin):
|
10
|
+
"""Empty key provider plugin."""
|
11
|
+
|
10
12
|
__namespace__ = "_dpapi_keyprovider_empty"
|
11
13
|
|
12
14
|
def check_compatible(self) -> None:
|
@@ -14,4 +16,5 @@ class EmptyKeyProviderPlugin(KeyProviderPlugin):
|
|
14
16
|
|
15
17
|
@export(output="yield")
|
16
18
|
def keys(self) -> Iterator[tuple[str, str]]:
|
19
|
+
"""Yield an empty string."""
|
17
20
|
yield self.__namespace__, ""
|
@@ -8,6 +8,8 @@ from dissect.target.plugins.os.windows.dpapi.keyprovider.keyprovider import (
|
|
8
8
|
|
9
9
|
|
10
10
|
class KeychainKeyProviderPlugin(KeyProviderPlugin):
|
11
|
+
"""Keychain key provider plugin."""
|
12
|
+
|
11
13
|
__namespace__ = "_dpapi_keyprovider_keychain"
|
12
14
|
|
13
15
|
def check_compatible(self) -> None:
|
@@ -15,6 +17,7 @@ class KeychainKeyProviderPlugin(KeyProviderPlugin):
|
|
15
17
|
|
16
18
|
@export(output="yield")
|
17
19
|
def keys(self) -> Iterator[tuple[str, str]]:
|
20
|
+
"""Yield keychain passphrases."""
|
18
21
|
for key in keychain.get_keys_for_provider("user") + keychain.get_keys_without_provider():
|
19
22
|
if key.key_type == keychain.KeyType.PASSPHRASE:
|
20
23
|
yield self.__namespace__, key.value
|
@@ -21,6 +21,8 @@ c_defaultpassword = cstruct().load(defaultpassword_def)
|
|
21
21
|
|
22
22
|
|
23
23
|
class LSADefaultPasswordKeyProviderPlugin(KeyProviderPlugin):
|
24
|
+
"""Windows LSA DefaultPassword key provider plugin."""
|
25
|
+
|
24
26
|
__namespace__ = "_dpapi_keyprovider_lsa_defaultpassword"
|
25
27
|
|
26
28
|
def check_compatible(self) -> None:
|
@@ -29,6 +31,7 @@ class LSADefaultPasswordKeyProviderPlugin(KeyProviderPlugin):
|
|
29
31
|
|
30
32
|
@export(output="yield")
|
31
33
|
def keys(self) -> Iterator[tuple[str, str]]:
|
34
|
+
"""Yield Windows LSA DefaultPassword strings."""
|
32
35
|
if default_pass := self.target.lsa._secrets.get("DefaultPassword"):
|
33
36
|
try:
|
34
37
|
value = c_defaultpassword.DefaultPassword(default_pass).data
|
@@ -314,8 +314,7 @@ class EnvironmentVariablePlugin(Plugin):
|
|
314
314
|
def user_env(self, user_sid: Optional[str] = None) -> OrderedDict[str, str]:
|
315
315
|
"""Return a dict of all found (user) environment variables.
|
316
316
|
|
317
|
-
If no ``user_sid
|
318
|
-
system environment variables.
|
317
|
+
If no ``user_sid`` is provided, this function will return just the system environment variables.
|
319
318
|
"""
|
320
319
|
return self._get_user_env_vars(user_sid)
|
321
320
|
|
@@ -3,13 +3,15 @@ from dissect.target.plugin import Plugin, export
|
|
3
3
|
|
4
4
|
|
5
5
|
class ExchangePlugin(Plugin):
|
6
|
+
"""Microsoft Exchange Server plugin."""
|
7
|
+
|
6
8
|
__namespace__ = "exchange"
|
7
9
|
|
8
10
|
def check_compatible(self) -> None:
|
9
11
|
if not len(self.install_paths()):
|
10
12
|
raise UnsupportedPluginError("No Exchange install path found")
|
11
13
|
|
12
|
-
def install_paths(self):
|
14
|
+
def install_paths(self) -> list[str]:
|
13
15
|
paths = []
|
14
16
|
key = "HKLM\\SOFTWARE\\Microsoft\\ExchangeServer"
|
15
17
|
for reg_key in self.target.registry.keys(key):
|
@@ -23,9 +25,9 @@ class ExchangePlugin(Plugin):
|
|
23
25
|
|
24
26
|
return paths
|
25
27
|
|
26
|
-
@export
|
27
|
-
def transport_agents(self):
|
28
|
-
"""
|
28
|
+
@export(output="none")
|
29
|
+
def transport_agents(self) -> None:
|
30
|
+
"""Print the content of the config file for Transport Agents for Microsoft Exchange.
|
29
31
|
|
30
32
|
A Transport Agent is additional software on a Microsoft Exchange server that allows for custom processing of
|
31
33
|
email messages that go through the transport pipeline.
|