dissect.target 3.19.dev13__py3-none-any.whl → 3.19.dev15__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,91 +1,111 @@
1
- from datetime import datetime
1
+ import re
2
+ from datetime import datetime, timezone
3
+ from typing import Iterator
2
4
 
3
5
  from dissect.target.exceptions import UnsupportedPluginError
6
+ from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
7
+ from dissect.target.helpers.fsutil import TargetPath
8
+ from dissect.target.helpers.record import create_extended_descriptor
4
9
  from dissect.target.plugin import export
5
10
  from dissect.target.plugins.apps.remoteaccess.remoteaccess import (
11
+ GENERIC_LOG_RECORD_FIELDS,
6
12
  RemoteAccessPlugin,
7
- RemoteAccessRecord,
8
13
  )
14
+ from dissect.target.plugins.general.users import UserDetails
9
15
 
10
16
 
11
17
  class AnydeskPlugin(RemoteAccessPlugin):
12
- """
13
- Anydesk plugin.
14
- """
18
+ """Anydesk plugin."""
15
19
 
16
20
  __namespace__ = "anydesk"
17
21
 
18
22
  # Anydesk logs when installed as a service
19
23
  SERVICE_GLOBS = [
20
- "/sysvol/ProgramData/AnyDesk/*.trace", # Standard client >= Windows 7
21
- "/sysvol/ProgramData/AnyDesk/ad_*/*.trace", # Custom client >= Windows 7
22
- "/var/log/anydesk*.trace", # Standard/Custom client Linux/MacOS
24
+ # Standard client >= Windows 7
25
+ "sysvol/ProgramData/AnyDesk/*.trace",
26
+ # Custom client >= Windows 7
27
+ "sysvol/ProgramData/AnyDesk/ad_*/*.trace",
28
+ # Windows XP / 2003
29
+ "sysvol/Documents and Settings/Public/AnyDesk/*.trace",
30
+ "sysvol/Documents and Settings/Public/AnyDesk/ad_*/*.trace",
31
+ # Standard/Custom client Linux/MacOS
32
+ "var/log/anydesk*/*.trace",
23
33
  ]
24
34
 
25
35
  # User specific Anydesk logs
26
36
  USER_GLOBS = [
27
- "appdata/roaming/AnyDesk/*.trace", # Standard client Windows
28
- "appdata/roaming/AnyDesk/ad_*/*.trace", # Custom client Windows
29
- ".anydesk/*.trace", # Standard client Linux/MacOS
30
- ".anydesk_ad_*/*.trace", # Custom client Linux/MacOS
37
+ # Standard client Windows
38
+ "AppData/Roaming/AnyDesk/*.trace",
39
+ # Custom client Windows
40
+ "AppData/Roaming/AnyDesk/ad_*/*.trace",
41
+ # Windows XP / 2003
42
+ "AppData/AnyDesk/*.trace",
43
+ # Standard client Linux/MacOS
44
+ ".anydesk/*.trace",
45
+ # Custom client Linux/MacOS
46
+ ".anydesk_ad_*/*.trace",
31
47
  ]
32
48
 
49
+ RemoteAccessLogRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
50
+ "remoteaccess/anydesk/log", GENERIC_LOG_RECORD_FIELDS
51
+ )
52
+
33
53
  def __init__(self, target):
34
54
  super().__init__(target)
35
55
 
36
- self.logfiles = []
56
+ self.trace_files: set[tuple[TargetPath, UserDetails]] = set()
37
57
 
38
- # Check service globs
58
+ # Service globs
39
59
  user = None
40
- for log_glob in self.SERVICE_GLOBS:
41
- for logfile in self.target.fs.glob(log_glob):
42
- self.logfiles.append([logfile, user])
60
+ for trace_glob in self.SERVICE_GLOBS:
61
+ for trace_file in self.target.fs.path().glob(trace_glob):
62
+ self.trace_files.add((trace_file, user))
43
63
 
44
- # Anydesk logs when as user
64
+ # User globs
45
65
  for user_details in self.target.user_details.all_with_home():
46
- for log_glob in self.USER_GLOBS:
47
- for logfile in user_details.home_path.glob(log_glob):
48
- self.logfiles.append([logfile, user_details.user])
66
+ for trace_glob in self.USER_GLOBS:
67
+ for trace_file in user_details.home_path.glob(trace_glob):
68
+ self.trace_files.add((trace_file, user_details.user))
49
69
 
50
70
  def check_compatible(self) -> None:
51
- if not (len(self.logfiles)):
52
- raise UnsupportedPluginError("No Anydesk logs found")
71
+ if not self.trace_files:
72
+ raise UnsupportedPluginError("No Anydesk trace files found on target")
53
73
 
54
- @export(record=RemoteAccessRecord)
55
- def logs(self):
56
- """Return the content of the AnyDesk logs.
74
+ @export(record=RemoteAccessLogRecord)
75
+ def logs(self) -> Iterator[RemoteAccessLogRecord]:
76
+ """Parse AnyDesk trace files.
57
77
 
58
78
  AnyDesk is a remote desktop application and can be used by adversaries to get (persistent) access to a machine.
59
- Log files (.trace files) are retrieved from various location based on OS and client type.
79
+ Log files (.trace files) can be stored on various locations, based on target OS and client type.
80
+ Timestamps in trace files do not carry a time zone designator (TZD) but are in fact UTC.
60
81
 
61
82
  References:
62
83
  - https://www.inversecos.com/2021/02/forensic-analysis-of-anydesk-logs.html
63
84
  - https://support.anydesk.com/knowledge/trace-files#trace-file-locations
64
85
  """
65
- for logfile, user in self.logfiles:
66
- logfile = self.target.fs.path(logfile)
67
-
68
- for line in logfile.open("rt"):
86
+ for trace_file, user in self.trace_files:
87
+ for line in trace_file.open("rt", errors="backslashreplace"):
69
88
  line = line.strip()
70
89
 
71
- # Skip empty lines
72
- if not line:
73
- continue
74
-
75
- if "* * * * * * * * * * * * * *" in line:
90
+ if not line or "* * * * * * * * * * * * * *" in line:
76
91
  continue
77
92
 
78
- level, ts_day, ts_time, description = line.split(" ", 3)
79
- description = f"{level} {description}"
80
- ts_time = ts_time.split(".")[0]
81
-
82
- timestamp = datetime.strptime(f"{ts_day} {ts_time}", "%Y-%m-%d %H:%M:%S")
83
-
84
- yield RemoteAccessRecord(
85
- ts=timestamp,
86
- tool="anydesk",
87
- logfile=str(logfile),
88
- description=description,
89
- _target=self.target,
90
- _user=user,
91
- )
93
+ try:
94
+ level, ts_date, ts_time, message = line.split(" ", 3)
95
+
96
+ timestamp = datetime.strptime(f"{ts_date} {ts_time}", "%Y-%m-%d %H:%M:%S.%f").replace(
97
+ tzinfo=timezone.utc
98
+ )
99
+ message = re.sub(r"\s\s+", " ", f"{level} {message}")
100
+
101
+ yield self.RemoteAccessLogRecord(
102
+ ts=timestamp,
103
+ message=message,
104
+ source=trace_file,
105
+ _target=self.target,
106
+ _user=user,
107
+ )
108
+
109
+ except ValueError as e:
110
+ self.target.log.warning("Could not parse log line in file %s: '%s'", trace_file, line)
111
+ self.target.log.debug("", exc_info=e)
@@ -2,14 +2,14 @@ from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExt
2
2
  from dissect.target.helpers.record import create_extended_descriptor
3
3
  from dissect.target.plugin import NamespacePlugin
4
4
 
5
- RemoteAccessRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
6
- "application/log/remoteaccess",
7
- [
8
- ("datetime", "ts"),
9
- ("string", "tool"),
10
- ("path", "logfile"),
11
- ("string", "description"),
12
- ],
5
+ GENERIC_LOG_RECORD_FIELDS = [
6
+ ("datetime", "ts"),
7
+ ("string", "message"),
8
+ ("path", "source"),
9
+ ]
10
+
11
+ RemoteAccessLogRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
12
+ "remoteaccess/log", GENERIC_LOG_RECORD_FIELDS
13
13
  )
14
14
 
15
15
 
@@ -1,60 +1,72 @@
1
1
  import re
2
- from datetime import datetime
2
+ from datetime import datetime, timezone
3
+ from typing import Iterator
3
4
 
4
5
  from dissect.target.exceptions import UnsupportedPluginError
6
+ from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
7
+ from dissect.target.helpers.fsutil import TargetPath
8
+ from dissect.target.helpers.record import create_extended_descriptor
5
9
  from dissect.target.plugin import export
6
10
  from dissect.target.plugins.apps.remoteaccess.remoteaccess import (
11
+ GENERIC_LOG_RECORD_FIELDS,
7
12
  RemoteAccessPlugin,
8
- RemoteAccessRecord,
9
13
  )
14
+ from dissect.target.plugins.general.users import UserDetails
10
15
 
11
16
  START_PATTERN = re.compile(r"^(\d{2}|\d{4})/")
12
17
 
13
18
 
14
- class TeamviewerPlugin(RemoteAccessPlugin):
15
- """
16
- Teamviewer plugin.
19
+ class TeamViewerPlugin(RemoteAccessPlugin):
20
+ """TeamViewer client plugin.
21
+
22
+ Resources:
23
+ - https://teamviewer.com/en/global/support/knowledge-base/teamviewer-classic/contact-support/find-your-log-files
24
+ - https://www.systoolsgroup.com/forensics/teamviewer/
25
+ - https://benleeyr.wordpress.com/2020/05/19/teamviewer-forensics-tested-on-v15/
17
26
  """
18
27
 
19
28
  __namespace__ = "teamviewer"
20
29
 
21
- # Teamviewer log when service (Windows)
22
- GLOBS = [
30
+ SYSTEM_GLOBS = [
23
31
  "sysvol/Program Files/TeamViewer/*.log",
24
32
  "sysvol/Program Files (x86)/TeamViewer/*.log",
25
33
  ]
26
34
 
35
+ USER_GLOBS = [
36
+ "AppData/Roaming/TeamViewer/teamviewer*_logfile.log",
37
+ ]
38
+
39
+ RemoteAccessLogRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
40
+ "remoteaccess/teamviewer/log", GENERIC_LOG_RECORD_FIELDS
41
+ )
42
+
27
43
  def __init__(self, target):
28
44
  super().__init__(target)
29
45
 
30
- self.logfiles = []
46
+ self.logfiles: list[list[TargetPath, UserDetails]] = []
31
47
 
32
- # Check service globs
33
- user = None
34
- for log_glob in self.GLOBS:
48
+ # Find system service log files.
49
+ for log_glob in self.SYSTEM_GLOBS:
35
50
  for logfile in self.target.fs.glob(log_glob):
36
- self.logfiles.append([logfile, user])
51
+ self.logfiles.append([logfile, None])
37
52
 
38
- # Teamviewer logs when as user (Windows)
53
+ # Find user log files.
39
54
  for user_details in self.target.user_details.all_with_home():
40
- for logfile in user_details.home_path.glob("appdata/roaming/teamviewer/teamviewer*_logfile.log"):
41
- self.logfiles.append([logfile, user_details.user])
55
+ for log_glob in self.USER_GLOBS:
56
+ for logfile in user_details.home_path.glob(log_glob):
57
+ self.logfiles.append([logfile, user_details])
42
58
 
43
59
  def check_compatible(self) -> None:
44
60
  if not len(self.logfiles):
45
61
  raise UnsupportedPluginError("No Teamviewer logs found")
46
62
 
47
- @export(record=RemoteAccessRecord)
48
- def logs(self):
49
- """Return the content of the TeamViewer logs.
50
-
51
- TeamViewer is a commercial remote desktop application. An adversary may use it to gain persistence on a
52
- system.
63
+ @export(record=RemoteAccessLogRecord)
64
+ def logs(self) -> Iterator[RemoteAccessLogRecord]:
65
+ """Yield TeamViewer client logs.
53
66
 
54
- References:
55
- - https://www.teamviewer.com/nl/
67
+ TeamViewer is a commercial remote desktop application. An adversary may use it to gain persistence on a system.
56
68
  """
57
- for logfile, user in self.logfiles:
69
+ for logfile, user_details in self.logfiles:
58
70
  logfile = self.target.fs.path(logfile)
59
71
 
60
72
  start_date = None
@@ -83,7 +95,7 @@ class TeamviewerPlugin(RemoteAccessPlugin):
83
95
  if not re.match(START_PATTERN, line):
84
96
  continue
85
97
 
86
- ts_day, ts_time, description = line.split(" ", 2)
98
+ ts_day, ts_time, message = line.split(" ", 2)
87
99
  ts_time = ts_time.split(".")[0]
88
100
 
89
101
  # Correct for use of : as millisecond separator
@@ -99,13 +111,14 @@ class TeamviewerPlugin(RemoteAccessPlugin):
99
111
  if ts_day.count("/") == 2 and len(ts_day.split("/")[0]) == 2:
100
112
  ts_day = "20" + ts_day
101
113
 
102
- timestamp = datetime.strptime(f"{ts_day} {ts_time}", "%Y/%m/%d %H:%M:%S")
114
+ timestamp = datetime.strptime(f"{ts_day} {ts_time}", "%Y/%m/%d %H:%M:%S").replace(
115
+ tzinfo=timezone.utc
116
+ )
103
117
 
104
- yield RemoteAccessRecord(
105
- tool="teamviewer",
118
+ yield self.RemoteAccessLogRecord(
106
119
  ts=timestamp,
107
- logfile=str(logfile),
108
- description=description,
120
+ message=message,
121
+ source=logfile,
109
122
  _target=self.target,
110
- _user=user,
123
+ _user=user_details.user if user_details else None,
111
124
  )
@@ -79,15 +79,15 @@ class WindowsPlugin(OSPlugin):
79
79
  self.target.log.debug("", exc_info=e)
80
80
 
81
81
  sysvol_drive = self.target.fs.mounts.get("sysvol")
82
- # Fallback mount the sysvol to C: if we didn't manage to mount it to any other drive letter
83
- if sysvol_drive and operator.countOf(self.target.fs.mounts.values(), sysvol_drive) == 1:
82
+ if not sysvol_drive:
83
+ self.target.log.warning("No sysvol drive found")
84
+ elif operator.countOf(self.target.fs.mounts.values(), sysvol_drive) == 1:
85
+ # Fallback mount the sysvol to C: if we didn't manage to mount it to any other drive letter
84
86
  if "c:" not in self.target.fs.mounts:
85
87
  self.target.log.debug("Unable to determine drive letter of sysvol, falling back to C:")
86
88
  self.target.fs.mount("c:", sysvol_drive)
87
89
  else:
88
90
  self.target.log.warning("Unknown drive letter for sysvol")
89
- else:
90
- self.target.log.warning("No sysvol drive found")
91
91
 
92
92
  @export(property=True)
93
93
  def hostname(self) -> Optional[str]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev13
3
+ Version: 3.19.dev15
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
@@ -128,9 +128,9 @@ dissect/target/plugins/apps/browser/iexplore.py,sha256=g_xw0toaiyjevxO8g9XPCOqc-
128
128
  dissect/target/plugins/apps/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
129
  dissect/target/plugins/apps/container/docker.py,sha256=KxQRbKGgxkf3YFBMa7fjeJ7qo8qjFys7zEmfQhDTnLw,15305
130
130
  dissect/target/plugins/apps/remoteaccess/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
- dissect/target/plugins/apps/remoteaccess/anydesk.py,sha256=lHtgINWXfVpPuCTRyQmT2ZO-1vkoqiXZ7coj8cZ8p4c,3185
132
- dissect/target/plugins/apps/remoteaccess/remoteaccess.py,sha256=UQDmDC4Y-KxYl_8kaAh6SG_BLJZ6SeGnxG0gyD8tzaE,833
133
- dissect/target/plugins/apps/remoteaccess/teamviewer.py,sha256=SiEH36HM2NvdPuCjfLjQcMDsluwkcHp_3io9SoY8qFk,4032
131
+ dissect/target/plugins/apps/remoteaccess/anydesk.py,sha256=IdijK3F6ppaB_IgKL-xDljlEbb8l9S2U0xSWKqK9xRs,4294
132
+ dissect/target/plugins/apps/remoteaccess/remoteaccess.py,sha256=DWXkRDVUpFr1icK2fYwSXdZD204Xz0yRuO7rcJOwIwc,825
133
+ dissect/target/plugins/apps/remoteaccess/teamviewer.py,sha256=0nGjgbzzrhESr1PeiXlGxYQ66-aOATO59CnVSwcGn4E,4889
134
134
  dissect/target/plugins/apps/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
135
  dissect/target/plugins/apps/shell/powershell.py,sha256=biPSMRWxPI6kRqP0-75yMtrw0Ti2Bzfl_xI3xbmmF48,2641
136
136
  dissect/target/plugins/apps/ssh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -254,7 +254,7 @@ dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wq89wRSFZSBsoKVCxjDofnC4yw9
254
254
  dissect/target/plugins/os/unix/log/messages.py,sha256=CXA-SkMPLaCgnTQg9nzII-7tO8Il_ENQmuYvDxo33rI,4698
255
255
  dissect/target/plugins/os/unix/log/utmp.py,sha256=1nPHIaBUHt_9z6PDrvyqg4huKLihUaWLrMmgMsbaeIo,7755
256
256
  dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
257
- dissect/target/plugins/os/windows/_os.py,sha256=Iu-xgEqtkycx1yDx4b_GL29pSz1Lew7lUYCByBOmTOE,13127
257
+ dissect/target/plugins/os/windows/_os.py,sha256=uBa0dVkFxDsxHAU3T23UEIOCgAx5R6cIpCgbGq3fflY,13131
258
258
  dissect/target/plugins/os/windows/activitiescache.py,sha256=Q2aILnhJ2rp2AwEbWwyBuSLjMbGqaYJTsavSbfkcFKE,6741
259
259
  dissect/target/plugins/os/windows/adpolicy.py,sha256=fULRFO_I_QxAn6G9SCwlLL-TLVliS13JEGnGotf7lSA,6983
260
260
  dissect/target/plugins/os/windows/amcache.py,sha256=ZZNOs3bILTf0AGkDkhoatndl0j39DXkstN7oOyxJECU,27188
@@ -344,10 +344,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
344
344
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
345
345
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
346
346
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
347
- dissect.target-3.19.dev13.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
348
- dissect.target-3.19.dev13.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
349
- dissect.target-3.19.dev13.dist-info/METADATA,sha256=oFZiiry3QZEqrgYijsGOlPjZn1DfUM3GBMdf8WZaIFc,12719
350
- dissect.target-3.19.dev13.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
351
- dissect.target-3.19.dev13.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
352
- dissect.target-3.19.dev13.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
353
- dissect.target-3.19.dev13.dist-info/RECORD,,
347
+ dissect.target-3.19.dev15.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
348
+ dissect.target-3.19.dev15.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
349
+ dissect.target-3.19.dev15.dist-info/METADATA,sha256=S5x1NtWPGpIryy_gl8s0sYJtuGIfVQ1mLytzBXwwDfE,12719
350
+ dissect.target-3.19.dev15.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
351
+ dissect.target-3.19.dev15.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
352
+ dissect.target-3.19.dev15.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
353
+ dissect.target-3.19.dev15.dist-info/RECORD,,