dissect.target 3.18.dev16__py3-none-any.whl → 3.19__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. dissect/target/filesystem.py +44 -25
  2. dissect/target/filesystems/config.py +32 -21
  3. dissect/target/filesystems/extfs.py +4 -0
  4. dissect/target/filesystems/itunes.py +1 -1
  5. dissect/target/filesystems/tar.py +1 -1
  6. dissect/target/filesystems/zip.py +81 -46
  7. dissect/target/helpers/config.py +22 -7
  8. dissect/target/helpers/configutil.py +69 -5
  9. dissect/target/helpers/cyber.py +4 -2
  10. dissect/target/helpers/fsutil.py +32 -4
  11. dissect/target/helpers/loaderutil.py +26 -7
  12. dissect/target/helpers/network_managers.py +22 -7
  13. dissect/target/helpers/record.py +37 -0
  14. dissect/target/helpers/record_modifier.py +23 -4
  15. dissect/target/helpers/shell_application_ids.py +732 -0
  16. dissect/target/helpers/utils.py +11 -0
  17. dissect/target/loader.py +1 -0
  18. dissect/target/loaders/ab.py +285 -0
  19. dissect/target/loaders/libvirt.py +40 -0
  20. dissect/target/loaders/mqtt.py +14 -1
  21. dissect/target/loaders/tar.py +8 -4
  22. dissect/target/loaders/utm.py +3 -0
  23. dissect/target/loaders/velociraptor.py +6 -6
  24. dissect/target/plugin.py +60 -3
  25. dissect/target/plugins/apps/browser/chrome.py +1 -0
  26. dissect/target/plugins/apps/browser/chromium.py +7 -5
  27. dissect/target/plugins/apps/browser/edge.py +1 -0
  28. dissect/target/plugins/apps/browser/firefox.py +82 -36
  29. dissect/target/plugins/apps/remoteaccess/anydesk.py +70 -50
  30. dissect/target/plugins/apps/remoteaccess/remoteaccess.py +8 -8
  31. dissect/target/plugins/apps/remoteaccess/teamviewer.py +46 -31
  32. dissect/target/plugins/apps/ssh/openssh.py +1 -1
  33. dissect/target/plugins/apps/ssh/ssh.py +177 -0
  34. dissect/target/plugins/apps/texteditor/__init__.py +0 -0
  35. dissect/target/plugins/apps/texteditor/texteditor.py +13 -0
  36. dissect/target/plugins/apps/texteditor/windowsnotepad.py +340 -0
  37. dissect/target/plugins/child/qemu.py +21 -0
  38. dissect/target/plugins/filesystem/ntfs/mft.py +132 -45
  39. dissect/target/plugins/filesystem/unix/capability.py +102 -87
  40. dissect/target/plugins/filesystem/walkfs.py +32 -21
  41. dissect/target/plugins/filesystem/yara.py +144 -23
  42. dissect/target/plugins/general/network.py +82 -0
  43. dissect/target/plugins/general/users.py +14 -10
  44. dissect/target/plugins/os/unix/_os.py +19 -5
  45. dissect/target/plugins/os/unix/bsd/freebsd/_os.py +3 -5
  46. dissect/target/plugins/os/unix/esxi/_os.py +29 -23
  47. dissect/target/plugins/os/unix/etc/etc.py +5 -8
  48. dissect/target/plugins/os/unix/history.py +3 -7
  49. dissect/target/plugins/os/unix/linux/_os.py +15 -14
  50. dissect/target/plugins/os/unix/linux/android/_os.py +15 -24
  51. dissect/target/plugins/os/unix/linux/redhat/_os.py +1 -1
  52. dissect/target/plugins/os/unix/locale.py +17 -6
  53. dissect/target/plugins/os/unix/shadow.py +47 -31
  54. dissect/target/plugins/os/windows/_os.py +4 -4
  55. dissect/target/plugins/os/windows/adpolicy.py +4 -1
  56. dissect/target/plugins/os/windows/catroot.py +1 -11
  57. dissect/target/plugins/os/windows/credential/__init__.py +0 -0
  58. dissect/target/plugins/os/windows/credential/lsa.py +174 -0
  59. dissect/target/plugins/os/windows/{sam.py → credential/sam.py} +5 -2
  60. dissect/target/plugins/os/windows/defender.py +6 -3
  61. dissect/target/plugins/os/windows/dpapi/blob.py +3 -0
  62. dissect/target/plugins/os/windows/dpapi/crypto.py +61 -23
  63. dissect/target/plugins/os/windows/dpapi/dpapi.py +127 -133
  64. dissect/target/plugins/os/windows/dpapi/keyprovider/__init__.py +0 -0
  65. dissect/target/plugins/os/windows/dpapi/keyprovider/credhist.py +21 -0
  66. dissect/target/plugins/os/windows/dpapi/keyprovider/empty.py +17 -0
  67. dissect/target/plugins/os/windows/dpapi/keyprovider/keychain.py +20 -0
  68. dissect/target/plugins/os/windows/dpapi/keyprovider/keyprovider.py +8 -0
  69. dissect/target/plugins/os/windows/dpapi/keyprovider/lsa.py +38 -0
  70. dissect/target/plugins/os/windows/dpapi/master_key.py +3 -0
  71. dissect/target/plugins/os/windows/jumplist.py +292 -0
  72. dissect/target/plugins/os/windows/lnk.py +96 -93
  73. dissect/target/plugins/os/windows/regf/shimcache.py +2 -2
  74. dissect/target/plugins/os/windows/regf/usb.py +179 -114
  75. dissect/target/plugins/os/windows/task_helpers/tasks_xml.py +1 -1
  76. dissect/target/plugins/os/windows/wua_history.py +1073 -0
  77. dissect/target/target.py +4 -3
  78. dissect/target/tools/fs.py +53 -15
  79. dissect/target/tools/fsutils.py +243 -0
  80. dissect/target/tools/info.py +11 -4
  81. dissect/target/tools/query.py +2 -2
  82. dissect/target/tools/shell.py +505 -333
  83. dissect/target/tools/utils.py +23 -2
  84. dissect/target/tools/yara.py +65 -0
  85. dissect/target/volumes/md.py +2 -2
  86. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/METADATA +11 -7
  87. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/RECORD +93 -74
  88. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/WHEEL +1 -1
  89. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/entry_points.txt +1 -0
  90. dissect/target/helpers/ssh.py +0 -177
  91. /dissect/target/plugins/os/windows/{credhist.py → credential/credhist.py} +0 -0
  92. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/COPYRIGHT +0 -0
  93. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/LICENSE +0 -0
  94. {dissect.target-3.18.dev16.dist-info → dissect.target-3.19.dist-info}/top_level.txt +0 -0
@@ -1,29 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import re
1
4
  import struct
5
+ from typing import Iterator
2
6
 
3
7
  from dissect.util.ts import wintimestamp
4
8
 
5
- from dissect.target.exceptions import RegistryValueNotFoundError, UnsupportedPluginError
9
+ from dissect.target.exceptions import (
10
+ RegistryKeyNotFoundError,
11
+ RegistryValueNotFoundError,
12
+ UnsupportedPluginError,
13
+ )
6
14
  from dissect.target.helpers.record import TargetRecordDescriptor
7
- from dissect.target.plugin import Plugin, export, internal
15
+ from dissect.target.helpers.regutil import VirtualKey
16
+ from dissect.target.plugin import Plugin, export
8
17
 
9
18
  UsbRegistryRecord = TargetRecordDescriptor(
10
19
  "windows/registry/usb",
11
20
  [
12
- ("string", "device_type"),
21
+ ("string", "type"),
13
22
  ("string", "serial"),
14
- ("string", "vid"),
15
- ("string", "pid"),
16
- ("string", "rev"),
17
- ("string", "containerid"),
23
+ ("string", "container_id"),
18
24
  ("string", "vendor"),
19
25
  ("string", "product"),
20
- ("string", "version"),
21
- ("string", "friendlyname"),
26
+ ("string", "revision"),
27
+ ("string", "friendly_name"),
22
28
  ("datetime", "first_insert"),
23
29
  ("datetime", "first_install"),
24
30
  ("datetime", "last_insert"),
25
31
  ("datetime", "last_removal"),
26
- ("string", "info_origin"),
32
+ ("string[]", "volumes"),
33
+ ("string[]", "mounts"),
34
+ ("string[]", "users"),
35
+ ("path", "source"),
27
36
  ],
28
37
  )
29
38
 
@@ -34,128 +43,184 @@ USB_DEVICE_PROPERTY_KEYS = {
34
43
  "last_removal": ("0067", "00000067"), # Windows 8 and higer. USB device last removal date.
35
44
  }
36
45
 
46
+ RE_DEVICE_NAME = re.compile(r"^(?P<type>.+?)&Ven_(?P<vendor>.+?)&Prod_(?P<product>.+?)(&Rev_(?P<revision>.+?))?$")
47
+
37
48
 
38
49
  class UsbPlugin(Plugin):
39
- """USB plugin."""
50
+ """Windows USB history plugin.
51
+
52
+ Parses Windows registry data about attached USB devices. Does not parse EVTX EventIDs
53
+ or ``C:\\Windows\\inf\\setupapi(.dev).log``.
40
54
 
41
- # USB device locations
55
+ To get a full picture of the USB history on a Windows machine, you should parse the
56
+ relevant EventIDs using the evtx plugin. For more research on event log USB forensics, see:
57
+ - https://www.researchgate.net/publication/318514858
58
+ - https://dfir.pubpub.org/pub/h78di10n/release/2
59
+ - https://www.senturean.com/posts/19_08_03_usb_storage_forensics_1/#1-system-events
60
+
61
+ Resources:
62
+ - https://hatsoffsecurity.com/2014/06/05/usb-forensics-pt-1-serial-number/
63
+ - http://www.swiftforensics.com/2013/11/windows-8-new-registry-artifacts-part-1.html
64
+ - https://www.sans.org/blog/the-truth-about-usb-device-serial-numbers/
65
+ """
66
+
67
+ # Stores history of mounted USB devices
42
68
  USB_STOR = "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USBSTOR"
43
- # DeviceContainers holds all USB information. Only present in windows 8 or higher
44
- DEVICE_CONTAINERS = "HKLM\\SYSTEM\\CurrentControlSet\\Control\\DeviceContainers"
45
- USB = "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USB"
46
- HID = "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\HID"
47
- SCSI = "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\SCSI"
48
69
 
49
- def check_compatible(self) -> None:
50
- if not len(list(self.target.registry.keys(self.USB_STOR))) > 0:
51
- raise UnsupportedPluginError(f"Registry key not found: {self.USB_STOR}")
70
+ # Stores the relation between a USB container_id and the FriendlyName of mounted volume(s) (Windows 7 and up)
71
+ PORTABLE_DEVICES = "HKLM\\SOFTWARE\\Microsoft\\Windows Portable Devices\\Devices"
52
72
 
53
- @internal
54
- def unpack_timestamps(self, usb_reg_properties):
55
- """
56
- Params:
57
- usb_reg_properties (Regf): A registry object with USB properties
58
- Returns:
59
- timestamps (Dict): A dict containing parsed timestamps within passed registry object
60
- """
61
- usb_reg_properties = usb_reg_properties.subkey("{83da6326-97a6-4088-9453-a1923f573b29}")
62
- timestamps = {}
63
-
64
- for device_property, usbstor_values in USB_DEVICE_PROPERTY_KEYS.items():
65
- for usb_val in usbstor_values:
66
- if usb_val in [x.name for x in usb_reg_properties.subkeys()]:
67
- version_key = usb_reg_properties.subkey(usb_val)
68
- if "00000000" in version_key.subkeys():
69
- data_value = version_key.subkey("00000000").value("Data").value
70
- else:
71
- data_value = version_key.value("(Default)").value
72
- timestamps[device_property] = wintimestamp(struct.unpack("<Q", data_value)[0])
73
- break
74
- else:
75
- timestamps[device_property] = None
76
- return timestamps
73
+ # Stores the most recent mapping of a mount letter and a container_id
74
+ MOUNT_LETTER_MAP = "HKLM\\SYSTEM\\MountedDevices"
77
75
 
78
- @internal
79
- def parse_device_name(self, device_name):
80
- device_info = device_name.split("&")
81
- device_type = device_info[0]
82
- vendor = device_info[1].split("Ven_")[1]
83
- product = device_info[2].split("Prod_")[1]
84
- version = None if len(device_info) < 4 else device_info[3].split("Rev_")[1]
76
+ # User history of mount points accesses in explorer.exe
77
+ USER_MOUNTS = "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Mountpoints2"
85
78
 
86
- return dict(device_type=device_type, vendor=vendor, product=product, version=version)
79
+ # Other artifacts we currently do not parse:
80
+ # - "sysvol\Windows\inf\setupapi(.dev).log"
81
+ # - "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USB"
82
+ # - "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\HID"
83
+ # - "HKLM\\SYSTEM\\CurrentControlSet\\Enum\\SCSI"
84
+ # - "HKLM\\SYSTEM\\CurrentControlSet\\Control\\DeviceContainers"
85
+ # - "SOFTWARE\Microsoft\Windows NT\CurrentVersion\EMDMgmt"
86
+
87
+ def check_compatible(self) -> None:
88
+ if not list(self.target.registry.keys(self.USB_STOR)):
89
+ raise UnsupportedPluginError(f"Registry key not found: {self.USB_STOR}")
87
90
 
88
91
  @export(record=UsbRegistryRecord)
89
- def usb(self):
90
- """Return information about attached USB devices.
91
-
92
- Use the registry to find information about USB devices that have been attached to the system, for example the
93
- HKLM\\SYSTEM\\CurrentControlSet\\Enum\\USBSTOR registry key.
94
-
95
- Yields UsbRegistryRecord with fields:
96
-
97
- .. code-block:: text
98
-
99
- hostname (string): The target hostname
100
- domain (string): The target domain
101
- type (string): Type of USB device
102
- serial (string): Serial number of USB storage device
103
- vid (string): Vendor ID of USB storage device
104
- pid (string): Product ID of the USB storage device
105
- rev (string): Version of the USB storage device
106
- containerid (string):
107
- friendlyname (string): Display name of the USB storage device
108
- first_insert (datetime): First insertion date of USB storage device
109
- first_install (datetime): First instalation date of USB storage device
110
- last_insert (datetime): Most recent insertion (arrival) date of USB storage device
111
- last_removal (datetime): Most recent removal (unplug) date of USB storage device
112
- info_origin (string): Location of info present in output
92
+ def usb(self) -> Iterator[UsbRegistryRecord]:
93
+ """Yields information about (historically) attached USB storage devices on Windows.
94
+
95
+ Uses the registry to find information about USB storage devices that have been attached to the system.
96
+ Also tries to find the past volume name and mount letters of the USB device and what user(s) interacted
97
+ with them using ``explorer.exe``.
113
98
  """
114
99
 
115
- for k in self.target.registry.keys(self.USB_STOR):
116
- info_origin = "\\".join((k.path, k.name))
117
- usb_stor = k.subkeys()
100
+ for key in self.target.registry.keys(self.USB_STOR):
101
+ for usb_type in key.subkeys():
102
+ try:
103
+ device_info = parse_device_name(usb_type.name)
104
+ except ValueError:
105
+ self.target.log.warning("Unable to parse USB device name: %s", usb_type.name)
106
+ device_info = {"type": None, "vendor": None, "product": None, "revision": None}
118
107
 
119
- for usb_type in usb_stor:
120
- device_info = self.parse_device_name(usb_type.name)
121
- usb_devices = usb_type.subkeys()
122
- for usb_device in usb_devices:
123
- properties = list(usb_device.subkeys())
108
+ for usb_device in usb_type.subkeys():
124
109
  serial = usb_device.name
110
+ friendly_name = None
111
+ container_id = None
112
+ timestamps = {
113
+ "first_install": None,
114
+ "first_insert": None,
115
+ "last_insert": None,
116
+ "last_removal": None,
117
+ }
118
+
125
119
  try:
126
- friendlyname = usb_device.value("FriendlyName").value
127
- # NOTE: make this more gracefull, windows 10 does not have the LogConf subkey
128
- timestamps = (
129
- self.unpack_timestamps(properties[2])
130
- if len(properties) == 3
131
- else self.unpack_timestamps(properties[1])
132
- )
133
- # ContainerIDs can be found back in USB and WdpBusEnumRoot
134
- containerid = usb_device.value("ContainerID").value
120
+ friendly_name = usb_device.value("FriendlyName").value
135
121
  except RegistryValueNotFoundError:
136
- friendlyname = None
137
- timestamps = {
138
- "first_install": None,
139
- "first_insert": None,
140
- "last_insert": None,
141
- "last_removal": None,
142
- }
143
- containerid = None
122
+ self.target.log.warning("No FriendlyName for USB with serial: %s", serial)
123
+ pass
124
+
125
+ try:
126
+ container_id = usb_device.value("ContainerID").value
127
+ except RegistryValueNotFoundError:
128
+ self.target.log.warning("No ContainerID for USB with serial: %s", serial)
129
+
130
+ try:
131
+ timestamps = unpack_timestamps(usb_device.subkey("Properties"))
132
+ except RegistryValueNotFoundError as e:
133
+ self.target.log.warning("Unable to parse USBSTOR registry properties for serial: %s", serial)
134
+ self.target.log.debug("", exc_info=e)
135
+
136
+ # We can check if any HKCU hive(s) are populated with the Volume GUID of the USB storage device.
137
+ # If a user has interacted with the mounted volume using explorer.exe we will get a match.
138
+ volumes = list(self.find_volumes(serial))
139
+ mounts = list(self.find_mounts(serial))
140
+ users = [
141
+ u.user.name for u in self.find_users([m[10:] for m in mounts if m.startswith("\\??\\Volume{")])
142
+ ]
144
143
 
145
144
  yield UsbRegistryRecord(
146
- device_type=device_info["device_type"],
147
- friendlyname=friendlyname,
145
+ friendly_name=friendly_name,
148
146
  serial=serial,
149
- vid=None,
150
- pid=None,
151
- vendor=device_info["vendor"],
152
- product=device_info["product"],
153
- version=device_info["version"],
154
- containerid=containerid,
155
- first_install=timestamps["first_install"],
156
- first_insert=timestamps["first_insert"],
157
- last_insert=timestamps["last_insert"], # AKA first arrival
158
- last_removal=timestamps["last_removal"],
159
- info_origin=info_origin,
147
+ container_id=container_id,
148
+ **device_info,
149
+ **timestamps,
150
+ volumes=volumes,
151
+ mounts=mounts,
152
+ users=users,
153
+ source=self.USB_STOR,
160
154
  _target=self.target,
161
155
  )
156
+
157
+ def find_volumes(self, serial: str) -> Iterator[str]:
158
+ """Attempts to find mounted volume names for the given serial."""
159
+ serial = serial.lower()
160
+ try:
161
+ for device in self.target.registry.key(self.PORTABLE_DEVICES).subkeys():
162
+ if serial in device.name.lower():
163
+ yield device.value("FriendlyName").value
164
+ except RegistryKeyNotFoundError:
165
+ pass
166
+
167
+ def find_mounts(self, serial: str) -> Iterator[str]:
168
+ """Attempts to find drive letters the given serial has been mounted on."""
169
+ serial = serial.lower()
170
+ try:
171
+ for mount in self.target.registry.key(self.MOUNT_LETTER_MAP).values():
172
+ try:
173
+ if serial in mount.value.decode("utf-16-le").lower():
174
+ yield mount.name.replace("\\DosDevices\\", "")
175
+ except UnicodeDecodeError:
176
+ pass
177
+ except RegistryKeyNotFoundError:
178
+ pass
179
+
180
+ def find_users(self, volume_guids: list[str]) -> Iterator[str]:
181
+ """Attempt to find Windows users that have interacted with the given volume GUIDs."""
182
+
183
+ for volume_guid in volume_guids:
184
+ try:
185
+ for key in self.target.registry.key(self.USER_MOUNTS + "\\" + volume_guid):
186
+ yield self.target.registry.get_user_details(key)
187
+ except RegistryKeyNotFoundError:
188
+ pass
189
+
190
+
191
+ def unpack_timestamps(usb_reg_properties: VirtualKey) -> dict[str, int]:
192
+ """Unpack relevant Windows timestamps from the provided USB registry properties subkey.
193
+
194
+ Args:
195
+ usb_reg_properties: A registry object with USB properties.
196
+
197
+ Returns:
198
+ A dict containing parsed timestamps within passed registry object.
199
+ """
200
+
201
+ usb_reg_properties = usb_reg_properties.subkey("{83da6326-97a6-4088-9453-a1923f573b29}")
202
+ timestamps = {}
203
+
204
+ for device_property, usbstor_values in USB_DEVICE_PROPERTY_KEYS.items():
205
+ for usb_val in usbstor_values:
206
+ if usb_val in [x.name for x in usb_reg_properties.subkeys()]:
207
+ version_key = usb_reg_properties.subkey(usb_val)
208
+ if "00000000" in version_key.subkeys():
209
+ data_value = version_key.subkey("00000000").value("Data").value
210
+ else:
211
+ data_value = version_key.value("(Default)").value
212
+ timestamps[device_property] = wintimestamp(struct.unpack("<Q", data_value)[0])
213
+ break
214
+ else:
215
+ timestamps[device_property] = None
216
+ return timestamps
217
+
218
+
219
+ def parse_device_name(device_name: str) -> dict[str, str]:
220
+ """Parse a registry device name into components."""
221
+
222
+ match = RE_DEVICE_NAME.match(device_name)
223
+ if not match:
224
+ raise ValueError(f"Unable to parse USB device name: {device_name}")
225
+
226
+ return match.groupdict()
@@ -189,7 +189,7 @@ class XmlTask:
189
189
  bytes: The raw XML data as string of the element if found, otherwise None.
190
190
  """
191
191
  data = self.task_element.find(xml_path) if xml_path else self.task_element
192
- if data:
192
+ if data is not None:
193
193
  return ElementTree.tostring(data, encoding="utf-8").strip()
194
194
 
195
195
  def get_triggers(self) -> Iterator[GroupedRecord]: