dissect.target 3.19.dev58__py3-none-any.whl → 3.20__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/container.py +1 -1
- dissect/target/exceptions.py +6 -5
- dissect/target/filesystem.py +2 -2
- dissect/target/filesystems/btrfs.py +14 -5
- dissect/target/filesystems/config.py +5 -1
- dissect/target/filesystems/extfs.py +5 -4
- dissect/target/filesystems/fat.py +22 -16
- dissect/target/filesystems/ffs.py +11 -4
- dissect/target/filesystems/jffs.py +12 -7
- dissect/target/filesystems/ntfs.py +22 -6
- dissect/target/filesystems/overlay.py +14 -4
- dissect/target/filesystems/smb.py +3 -3
- dissect/target/filesystems/squashfs.py +4 -4
- dissect/target/filesystems/vmfs.py +4 -4
- dissect/target/filesystems/xfs.py +15 -8
- dissect/target/helpers/compat/path_common.py +5 -5
- dissect/target/helpers/configutil.py +128 -32
- dissect/target/helpers/cyber.py +2 -0
- dissect/target/helpers/data/windowsZones.xml +19 -23
- dissect/target/helpers/docs.py +1 -1
- dissect/target/helpers/keychain.py +2 -0
- dissect/target/helpers/mount.py +2 -1
- dissect/target/helpers/record.py +29 -2
- dissect/target/helpers/record_modifier.py +5 -1
- dissect/target/helpers/regutil.py +56 -26
- dissect/target/loader.py +1 -1
- dissect/target/loaders/mqtt.py +104 -9
- dissect/target/loaders/proxmox.py +68 -0
- dissect/target/loaders/vma.py +1 -1
- dissect/target/loaders/xva.py +1 -1
- dissect/target/plugin.py +24 -21
- 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/container/docker.py +48 -32
- dissect/target/plugins/apps/editor/__init__.py +0 -0
- dissect/target/plugins/apps/editor/editor.py +23 -0
- dissect/target/plugins/apps/{texteditor → editor}/windowsnotepad.py +40 -31
- dissect/target/plugins/apps/other/__init__.py +0 -0
- dissect/target/plugins/apps/other/env.py +56 -0
- dissect/target/plugins/apps/shell/powershell.py +6 -2
- dissect/target/plugins/apps/shell/wget.py +91 -0
- dissect/target/plugins/apps/ssh/openssh.py +2 -0
- dissect/target/plugins/apps/ssh/opensshd.py +2 -0
- dissect/target/plugins/apps/virtualization/__init__.py +0 -0
- dissect/target/plugins/apps/virtualization/vmware_workstation.py +61 -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/parallels.py +68 -0
- dissect/target/plugins/child/proxmox.py +23 -0
- dissect/target/plugins/child/virtuozzo.py +12 -8
- dissect/target/plugins/child/vmware_workstation.py +23 -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 +10 -6
- 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 +18 -5
- dissect/target/plugins/general/network.py +20 -5
- dissect/target/plugins/general/osinfo.py +1 -0
- dissect/target/plugins/general/plugins.py +53 -10
- dissect/target/plugins/os/unix/_os.py +70 -44
- dissect/target/plugins/os/unix/applications.py +78 -0
- dissect/target/plugins/os/unix/bsd/citrix/history.py +2 -0
- dissect/target/plugins/os/unix/bsd/osx/_os.py +4 -21
- dissect/target/plugins/os/unix/bsd/osx/network.py +92 -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/_os.py +8 -10
- 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/debian/proxmox/__init__.py +0 -0
- dissect/target/plugins/os/unix/linux/debian/proxmox/_os.py +141 -0
- dissect/target/plugins/os/unix/linux/debian/proxmox/vm.py +29 -0
- dissect/target/plugins/os/unix/linux/debian/snap.py +79 -0
- dissect/target/plugins/os/unix/linux/environ.py +2 -0
- dissect/target/plugins/os/unix/linux/fortios/_os.py +74 -63
- 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/{helpers → plugins/os/unix/linux}/network_managers.py +11 -9
- 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 +351 -38
- dissect/target/plugins/os/unix/log/journal.py +123 -101
- dissect/target/plugins/os/unix/log/lastlog.py +5 -3
- dissect/target/plugins/os/unix/log/messages.py +51 -27
- dissect/target/plugins/os/unix/log/utmp.py +52 -71
- dissect/target/plugins/os/unix/packagemanager.py +5 -38
- dissect/target/plugins/os/unix/shadow.py +3 -1
- dissect/target/plugins/os/unix/trash.py +132 -0
- dissect/target/plugins/os/windows/_os.py +22 -41
- 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 +4 -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 +68 -19
- 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/mssql.py +103 -0
- dissect/target/plugins/os/windows/log/pfro.py +2 -1
- dissect/target/plugins/os/windows/network.py +380 -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/applications.py +62 -0
- 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/shellbags.py +351 -345
- 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 +5 -2
- 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/wer.py +21 -6
- dissect/target/plugins/os/windows/wua_history.py +0 -1
- dissect/target/target.py +13 -8
- dissect/target/tools/dump/utils.py +4 -0
- dissect/target/tools/fsutils.py +1 -1
- dissect/target/tools/info.py +1 -1
- dissect/target/tools/mount.py +15 -5
- dissect/target/tools/query.py +15 -9
- dissect/target/tools/shell.py +98 -9
- dissect/target/tools/utils.py +7 -7
- dissect/target/volume.py +4 -4
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/METADATA +6 -2
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/RECORD +176 -160
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/WHEEL +1 -1
- dissect/target/helpers/targetd.py +0 -58
- dissect/target/loaders/targetd.py +0 -223
- dissect/target/plugins/apps/texteditor/texteditor.py +0 -13
- dissect/target/plugins/os/unix/etc.py +0 -9
- /dissect/target/plugins/apps/{texteditor → database}/__init__.py +0 -0
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/LICENSE +0 -0
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.19.dev58.dist-info → dissect.target-3.20.dist-info}/top_level.txt +0 -0
@@ -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
|
@@ -517,6 +517,7 @@ class MicrosoftDefenderPlugin(plugin.Plugin):
|
|
517
517
|
regf_mtime=exclusion_type_subkey.timestamp,
|
518
518
|
type=exclusion_type,
|
519
519
|
value=exclusion_value,
|
520
|
+
_target=self.target,
|
520
521
|
)
|
521
522
|
|
522
523
|
def _mplog_processimage(self, data: dict) -> Iterator[DefenderMPLogProcessImageRecord]:
|
@@ -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.
|
@@ -1,6 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import struct
|
1
4
|
from datetime import datetime
|
2
|
-
from typing import
|
5
|
+
from typing import Iterator
|
3
6
|
|
7
|
+
from dissect.util.sid import read_sid
|
4
8
|
from dissect.util.ts import from_unix
|
5
9
|
|
6
10
|
from dissect.target.exceptions import RegistryError, UnsupportedPluginError
|
@@ -8,7 +12,10 @@ from dissect.target.helpers.descriptor_extensions import (
|
|
8
12
|
RegistryRecordDescriptorExtension,
|
9
13
|
UserRecordDescriptorExtension,
|
10
14
|
)
|
11
|
-
from dissect.target.helpers.record import
|
15
|
+
from dissect.target.helpers.record import (
|
16
|
+
TargetRecordDescriptor,
|
17
|
+
create_extended_descriptor,
|
18
|
+
)
|
12
19
|
from dissect.target.plugin import Plugin, export
|
13
20
|
|
14
21
|
UserRegistryRecordDescriptor = create_extended_descriptor(
|
@@ -111,6 +118,15 @@ WinSockNamespaceProviderRecord = UserRegistryRecordDescriptor(
|
|
111
118
|
],
|
112
119
|
)
|
113
120
|
|
121
|
+
ComputerSidRecord = TargetRecordDescriptor(
|
122
|
+
"windows/sid/computer",
|
123
|
+
[
|
124
|
+
("datetime", "ts"),
|
125
|
+
("string", "sidtype"),
|
126
|
+
("string", "sid"),
|
127
|
+
],
|
128
|
+
)
|
129
|
+
|
114
130
|
|
115
131
|
class GenericPlugin(Plugin):
|
116
132
|
"""Generic Windows plugin.
|
@@ -123,12 +139,12 @@ class GenericPlugin(Plugin):
|
|
123
139
|
raise UnsupportedPluginError("Unsupported Plugin")
|
124
140
|
|
125
141
|
@export(property=True)
|
126
|
-
def ntversion(self):
|
142
|
+
def ntversion(self) -> str | None:
|
127
143
|
"""Return the Windows NT version."""
|
128
144
|
return self.target._os._nt_version()
|
129
145
|
|
130
146
|
@export(output="yield")
|
131
|
-
def pathenvironment(self):
|
147
|
+
def pathenvironment(self) -> Iterator[str]:
|
132
148
|
"""Return the content of the Windows PATH environment variable.
|
133
149
|
|
134
150
|
PATH is an environment variable on an operating system that specifies a set of directories where executable
|
@@ -142,7 +158,7 @@ class GenericPlugin(Plugin):
|
|
142
158
|
yield r.value("Path").value
|
143
159
|
|
144
160
|
@export(property=True)
|
145
|
-
def domain(self):
|
161
|
+
def domain(self) -> str | None:
|
146
162
|
"""Return the domain name.
|
147
163
|
|
148
164
|
Corporate Windows systems are usually connected to a domain (active directory).
|
@@ -167,7 +183,7 @@ class GenericPlugin(Plugin):
|
|
167
183
|
continue
|
168
184
|
|
169
185
|
@export(property=True)
|
170
|
-
def activity(self) ->
|
186
|
+
def activity(self) -> datetime | None:
|
171
187
|
"""Return last seen activity based on filesystem timestamps."""
|
172
188
|
last_seen = 0
|
173
189
|
|
@@ -193,7 +209,7 @@ class GenericPlugin(Plugin):
|
|
193
209
|
return from_unix(last_seen)
|
194
210
|
|
195
211
|
@export(property=True)
|
196
|
-
def install_date(self) ->
|
212
|
+
def install_date(self) -> datetime | None:
|
197
213
|
"""Returns the install date of the system.
|
198
214
|
|
199
215
|
The value of the registry key is stored as a Unix epoch timestamp.
|
@@ -211,7 +227,7 @@ class GenericPlugin(Plugin):
|
|
211
227
|
return
|
212
228
|
|
213
229
|
@export(record=AppInitRecord)
|
214
|
-
def appinit(self):
|
230
|
+
def appinit(self) -> Iterator[AppInitRecord]:
|
215
231
|
"""Return all available Application Initial (AppInit) DLLs registry key values.
|
216
232
|
|
217
233
|
AppInit_DLLs is a mechanism that allows an arbitrary list of DLLs to be loaded into each user mode process on
|
@@ -258,7 +274,7 @@ class GenericPlugin(Plugin):
|
|
258
274
|
continue
|
259
275
|
|
260
276
|
@export(record=KnownDllRecord)
|
261
|
-
def knowndlls(self):
|
277
|
+
def knowndlls(self) -> Iterator[KnownDllRecord]:
|
262
278
|
"""Return all available KnownDLLs registry key values.
|
263
279
|
|
264
280
|
The KnownDLLs registry key values are used to cache frequently used system DLLs. Initially, it was added to
|
@@ -287,7 +303,7 @@ class GenericPlugin(Plugin):
|
|
287
303
|
pass
|
288
304
|
|
289
305
|
@export(record=SessionManagerRecord)
|
290
|
-
def sessionmanager(self):
|
306
|
+
def sessionmanager(self) -> Iterator[SessionManagerRecord]:
|
291
307
|
"""Return interesting Session Manager (Smss.exe) registry key entries.
|
292
308
|
|
293
309
|
Session Manager (Smss.exe) is the first user-mode process started by the kernel and performs several tasks,
|
@@ -339,7 +355,7 @@ class GenericPlugin(Plugin):
|
|
339
355
|
)
|
340
356
|
|
341
357
|
@export(record=NullSessionPipeRecord)
|
342
|
-
def nullsessionpipes(self):
|
358
|
+
def nullsessionpipes(self) -> Iterator[NullSessionPipeRecord]:
|
343
359
|
"""Return the NullSessionPipes registry key value.
|
344
360
|
|
345
361
|
The NullSessionPipes registry key value specifies server pipes and shared folders that are excluded from the
|
@@ -367,7 +383,7 @@ class GenericPlugin(Plugin):
|
|
367
383
|
continue
|
368
384
|
|
369
385
|
@export(record=NdisRecord)
|
370
|
-
def ndis(self):
|
386
|
+
def ndis(self) -> Iterator[NdisRecord]:
|
371
387
|
"""Return network registry key entries."""
|
372
388
|
key = "HKLM\\System\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
373
389
|
for r in self.target.registry.keys(key):
|
@@ -401,7 +417,7 @@ class GenericPlugin(Plugin):
|
|
401
417
|
)
|
402
418
|
|
403
419
|
@export(record=CommandProcAutoRunRecord)
|
404
|
-
def commandprocautorun(self):
|
420
|
+
def commandprocautorun(self) -> Iterator[CommandProcAutoRunRecord]:
|
405
421
|
"""Return all available Command Processor (cmd.exe) AutoRun registry key values.
|
406
422
|
|
407
423
|
The Command Processor AutoRun registry key values contain commands that are run each time the Command Processor
|
@@ -435,7 +451,7 @@ class GenericPlugin(Plugin):
|
|
435
451
|
continue
|
436
452
|
|
437
453
|
@export(record=AlternateShellRecord)
|
438
|
-
def alternateshell(self):
|
454
|
+
def alternateshell(self) -> Iterator[AlternateShellRecord]:
|
439
455
|
"""Return the AlternateShell registry key value.
|
440
456
|
|
441
457
|
The AlternateShell registry key, HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Safeboot, specifies the
|
@@ -459,7 +475,7 @@ class GenericPlugin(Plugin):
|
|
459
475
|
)
|
460
476
|
|
461
477
|
@export(record=BootShellRecord)
|
462
|
-
def bootshell(self):
|
478
|
+
def bootshell(self) -> Iterator[BootShellRecord]:
|
463
479
|
"""Return the BootShell registry key entry.
|
464
480
|
|
465
481
|
Usually contains a path to bootim.exe which is Windows's recovery menu.
|
@@ -483,7 +499,7 @@ class GenericPlugin(Plugin):
|
|
483
499
|
)
|
484
500
|
|
485
501
|
@export(record=FileRenameOperationRecord)
|
486
|
-
def filerenameop(self):
|
502
|
+
def filerenameop(self) -> Iterator[FileRenameOperationRecord]:
|
487
503
|
"""Return all pending file rename operations.
|
488
504
|
|
489
505
|
The PendingFileRenameOperations registry key value contains information about files that will be renamed on
|
@@ -513,7 +529,7 @@ class GenericPlugin(Plugin):
|
|
513
529
|
)
|
514
530
|
|
515
531
|
@export(record=WinRarRecord)
|
516
|
-
def winrar(self):
|
532
|
+
def winrar(self) -> Iterator[WinRarRecord]:
|
517
533
|
"""Return all available WinRAR history registry key values."""
|
518
534
|
keys = [
|
519
535
|
"HKEY_CURRENT_USER\\Software\\WinRAR\\ArcHistory",
|
@@ -534,7 +550,7 @@ class GenericPlugin(Plugin):
|
|
534
550
|
)
|
535
551
|
|
536
552
|
@export(record=WinSockNamespaceProviderRecord)
|
537
|
-
def winsocknamespaceprovider(self):
|
553
|
+
def winsocknamespaceprovider(self) -> Iterator[WinSockNamespaceProviderRecord]:
|
538
554
|
"""Return available protocols stored in the Winsock catalog database.
|
539
555
|
|
540
556
|
References:
|
@@ -562,7 +578,7 @@ class GenericPlugin(Plugin):
|
|
562
578
|
)
|
563
579
|
|
564
580
|
@export(property=True)
|
565
|
-
def codepage(self) ->
|
581
|
+
def codepage(self) -> str | None:
|
566
582
|
"""Returns the current active codepage on the system."""
|
567
583
|
|
568
584
|
key = "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage"
|
@@ -571,3 +587,36 @@ class GenericPlugin(Plugin):
|
|
571
587
|
return self.target.registry.key(key).value("ACP").value
|
572
588
|
except RegistryError:
|
573
589
|
pass
|
590
|
+
|
591
|
+
@export(record=ComputerSidRecord)
|
592
|
+
def sid(self) -> Iterator[ComputerSidRecord]:
|
593
|
+
"""Return the machine- and optional domain SID of the system."""
|
594
|
+
|
595
|
+
try:
|
596
|
+
key = self.target.registry.key("HKLM\\SAM\\SAM\\Domains\\Account")
|
597
|
+
|
598
|
+
# The machine SID is stored in the last 12 bytes of the V value as little-endian
|
599
|
+
# The machine SID differs from a 'normal' binary SID as only holds 3 values and lacks a prefix / Revision
|
600
|
+
# NOTE: Consider moving this to dissect.util.sid if we encounter this more often
|
601
|
+
sid = struct.unpack_from("<III", key.value("V").value, -12)
|
602
|
+
|
603
|
+
yield ComputerSidRecord(
|
604
|
+
ts=key.timestamp,
|
605
|
+
sidtype="Machine",
|
606
|
+
sid=f"S-1-5-21-{sid[0]}-{sid[1]}-{sid[2]}",
|
607
|
+
_target=self.target,
|
608
|
+
)
|
609
|
+
except (RegistryError, struct.error):
|
610
|
+
pass
|
611
|
+
|
612
|
+
try:
|
613
|
+
key = self.target.registry.key("HKLM\\SECURITY\\Policy\\PolMachineAccountS")
|
614
|
+
|
615
|
+
yield ComputerSidRecord(
|
616
|
+
ts=key.timestamp,
|
617
|
+
sidtype="Domain",
|
618
|
+
sid=read_sid(key.value("(Default)").value),
|
619
|
+
_target=self.target,
|
620
|
+
)
|
621
|
+
except (RegistryError, struct.error):
|
622
|
+
pass
|
@@ -118,6 +118,8 @@ def parse_lnk_file(target: Target, lnk_file: Lnk, lnk_path: TargetPath) -> Itera
|
|
118
118
|
|
119
119
|
|
120
120
|
class LnkPlugin(Plugin):
|
121
|
+
"""Windows lnk plugin."""
|
122
|
+
|
121
123
|
def __init__(self, target: Target) -> None:
|
122
124
|
super().__init__(target)
|
123
125
|
self.folders = ["programdata", "users", "windows"]
|
@@ -1,3 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Iterator
|
4
|
+
|
1
5
|
from dissect.target.exceptions import UnsupportedPluginError
|
2
6
|
from dissect.target.helpers.localeutil import normalize_language, normalize_timezone
|
3
7
|
from dissect.target.helpers.record import TargetRecordDescriptor
|
@@ -13,6 +17,8 @@ WindowsKeyboardRecord = TargetRecordDescriptor(
|
|
13
17
|
|
14
18
|
|
15
19
|
class LocalePlugin(Plugin):
|
20
|
+
"""Windows locale plugin."""
|
21
|
+
|
16
22
|
def __init__(self, target):
|
17
23
|
super().__init__(target)
|
18
24
|
self.LANG_DICT = {
|
@@ -26,7 +32,7 @@ class LocalePlugin(Plugin):
|
|
26
32
|
raise UnsupportedPluginError("Unsupported Plugin")
|
27
33
|
|
28
34
|
@export(record=WindowsKeyboardRecord)
|
29
|
-
def keyboard(self):
|
35
|
+
def keyboard(self) -> Iterator[WindowsKeyboardRecord]:
|
30
36
|
"""Yield records of installed keyboards on the system."""
|
31
37
|
found_keyboards = []
|
32
38
|
for key in self.target.registry.keys("HKCU\\Keyboard Layout\\Preload"):
|
@@ -41,7 +47,7 @@ class LocalePlugin(Plugin):
|
|
41
47
|
)
|
42
48
|
|
43
49
|
@export(property=True)
|
44
|
-
def language(self):
|
50
|
+
def language(self) -> str | None:
|
45
51
|
"""Get a list of installed languages on the system."""
|
46
52
|
# HKCU\\Control Panel\\International\\User Profile" Languages
|
47
53
|
found_languages = []
|
@@ -53,6 +59,6 @@ class LocalePlugin(Plugin):
|
|
53
59
|
return found_languages
|
54
60
|
|
55
61
|
@export(property=True)
|
56
|
-
def timezone(self):
|
62
|
+
def timezone(self) -> str | None:
|
57
63
|
"""Get the configured timezone of the system in IANA TZ standard format."""
|
58
64
|
return normalize_timezone(self.target.datetime.tzinfo.name)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from functools import lru_cache
|
2
2
|
from pathlib import Path
|
3
|
+
from typing import Iterator
|
3
4
|
|
4
5
|
from dissect.etl.etl import ETL, Event
|
5
6
|
|
@@ -65,7 +66,7 @@ class EtlRecordBuilder:
|
|
65
66
|
|
66
67
|
|
67
68
|
class EtlPlugin(Plugin):
|
68
|
-
"""Plugin for
|
69
|
+
"""Plugin for parsing Windows ETL Files (``*.etl``)."""
|
69
70
|
|
70
71
|
__namespace__ = "etl"
|
71
72
|
|
@@ -108,7 +109,7 @@ class EtlPlugin(Plugin):
|
|
108
109
|
yield from etl_records
|
109
110
|
|
110
111
|
@export(record=DynamicDescriptor(["datetime"]))
|
111
|
-
def etl(self):
|
112
|
+
def etl(self) -> Iterator[DynamicDescriptor]:
|
112
113
|
"""Return the contents of the ETL files generated at last boot and last shutdown.
|
113
114
|
|
114
115
|
An event trace log (.etl) file, also known as a trace log, stores the trace messages generated during one or
|
@@ -135,7 +136,7 @@ class EtlPlugin(Plugin):
|
|
135
136
|
yield from getattr(self, etl_plugin)()
|
136
137
|
|
137
138
|
@export(record=DynamicDescriptor(["datetime"]))
|
138
|
-
def shutdown(self):
|
139
|
+
def shutdown(self) -> Iterator[DynamicDescriptor]:
|
139
140
|
"""Return the contents of the ETL files created at last shutdown.
|
140
141
|
|
141
142
|
The plugin reads the content from the ShutdownCKCL.etl file or the ShutdownPerfDiagLogger.etl file (depending
|
@@ -155,7 +156,7 @@ class EtlPlugin(Plugin):
|
|
155
156
|
yield from self.read_etl_files(self.PATHS["shutdown"])
|
156
157
|
|
157
158
|
@export(record=DynamicDescriptor(["datetime"]))
|
158
|
-
def boot(self):
|
159
|
+
def boot(self) -> Iterator[DynamicDescriptor]:
|
159
160
|
"""Return the contents of the ETL files created at last boot.
|
160
161
|
|
161
162
|
The plugin reads the content from the BootCKCL.etl file or the BootPerfDiagLogger.etl file (depending
|
@@ -1,7 +1,9 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import fnmatch
|
2
4
|
import re
|
3
5
|
from pathlib import Path
|
4
|
-
from typing import Any, BinaryIO,
|
6
|
+
from typing import Any, BinaryIO, Iterator
|
5
7
|
|
6
8
|
from dissect.eventlog import evt
|
7
9
|
from flow.record import Record
|
@@ -48,7 +50,7 @@ class WindowsEventlogsMixin:
|
|
48
50
|
LOGS_DIR_PATH = None
|
49
51
|
|
50
52
|
@plugin.internal
|
51
|
-
def get_logs(self, filename_glob="*") ->
|
53
|
+
def get_logs(self, filename_glob="*") -> list[Path]:
|
52
54
|
file_paths = []
|
53
55
|
file_paths.extend(self.get_logs_from_dir(self.LOGS_DIR_PATH, filename_glob=filename_glob))
|
54
56
|
|
@@ -66,7 +68,7 @@ class WindowsEventlogsMixin:
|
|
66
68
|
return file_paths
|
67
69
|
|
68
70
|
@plugin.internal
|
69
|
-
def get_logs_from_dir(self, logs_dir: str, filename_glob: str = "*") ->
|
71
|
+
def get_logs_from_dir(self, logs_dir: str, filename_glob: str = "*") -> list[Path]:
|
70
72
|
file_paths = []
|
71
73
|
logs_dir = self.target.fs.path(logs_dir)
|
72
74
|
if logs_dir.exists():
|
@@ -76,7 +78,7 @@ class WindowsEventlogsMixin:
|
|
76
78
|
return file_paths
|
77
79
|
|
78
80
|
@plugin.internal
|
79
|
-
def get_logs_from_registry(self, filename_glob: str = "*") ->
|
81
|
+
def get_logs_from_registry(self, filename_glob: str = "*") -> list[Path]:
|
80
82
|
# compile glob into case-insensitive regex
|
81
83
|
filename_regex = re.compile(fnmatch.translate(filename_glob), re.IGNORECASE)
|
82
84
|
|
@@ -112,6 +114,8 @@ class WindowsEventlogsMixin:
|
|
112
114
|
|
113
115
|
|
114
116
|
class EvtPlugin(WindowsEventlogsMixin, plugin.Plugin):
|
117
|
+
"""Windows ``.evt`` event log plugin."""
|
118
|
+
|
115
119
|
LOGS_DIR_PATH = "sysvol/windows/system32/config"
|
116
120
|
|
117
121
|
NEEDLE = b"LfLe"
|
@@ -120,8 +124,8 @@ class EvtPlugin(WindowsEventlogsMixin, plugin.Plugin):
|
|
120
124
|
@plugin.arg("--logs-dir", help="logs directory to scan")
|
121
125
|
@plugin.arg("--log-file-glob", default=EVT_GLOB, help="glob pattern to match a log file name")
|
122
126
|
@plugin.export(record=EvtRecordDescriptor)
|
123
|
-
def evt(self, log_file_glob: str = EVT_GLOB, logs_dir:
|
124
|
-
"""Parse Windows Eventlog files (
|
127
|
+
def evt(self, log_file_glob: str = EVT_GLOB, logs_dir: str | None = None) -> Iterator[EvtRecordDescriptor]:
|
128
|
+
"""Parse Windows Eventlog files (``*.evt``).
|
125
129
|
|
126
130
|
Yields dynamically created records based on the fields in the event.
|
127
131
|
At least contains the following fields:
|
@@ -174,7 +178,7 @@ class EvtPlugin(WindowsEventlogsMixin, plugin.Plugin):
|
|
174
178
|
)
|
175
179
|
|
176
180
|
@plugin.export(record=EvtRecordDescriptor)
|
177
|
-
def scraped_evt(self) ->
|
181
|
+
def scraped_evt(self) -> Iterator[EvtRecordDescriptor]:
|
178
182
|
"""Yields EVT log file records scraped from target disks"""
|
179
183
|
yield from self.target.scrape.scrape_chunks_from_disks(
|
180
184
|
needle=self.NEEDLE,
|
@@ -189,6 +193,6 @@ class EvtPlugin(WindowsEventlogsMixin, plugin.Plugin):
|
|
189
193
|
fh.seek(offset - 4)
|
190
194
|
return fh.read(chunk_size)
|
191
195
|
|
192
|
-
def _parse_chunk(self, _, chunk: bytes) ->
|
196
|
+
def _parse_chunk(self, _, chunk: bytes) -> Iterator[Record]:
|
193
197
|
for record in evt.parse_chunk(chunk):
|
194
198
|
yield self._build_record(record)
|