dissect.target 3.19.dev13__py3-none-any.whl → 3.19.dev15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dissect/target/plugins/apps/remoteaccess/anydesk.py +70 -50
- dissect/target/plugins/apps/remoteaccess/remoteaccess.py +8 -8
- dissect/target/plugins/apps/remoteaccess/teamviewer.py +44 -31
- dissect/target/plugins/os/windows/_os.py +4 -4
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/METADATA +1 -1
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/RECORD +11 -11
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/LICENSE +0 -0
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/WHEEL +0 -0
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/top_level.txt +0 -0
@@ -1,91 +1,111 @@
|
|
1
|
-
|
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
|
-
|
21
|
-
"
|
22
|
-
|
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
|
-
|
28
|
-
"
|
29
|
-
|
30
|
-
"
|
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.
|
56
|
+
self.trace_files: set[tuple[TargetPath, UserDetails]] = set()
|
37
57
|
|
38
|
-
#
|
58
|
+
# Service globs
|
39
59
|
user = None
|
40
|
-
for
|
41
|
-
for
|
42
|
-
self.
|
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
|
-
#
|
64
|
+
# User globs
|
45
65
|
for user_details in self.target.user_details.all_with_home():
|
46
|
-
for
|
47
|
-
for
|
48
|
-
self.
|
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
|
52
|
-
raise UnsupportedPluginError("No Anydesk
|
71
|
+
if not self.trace_files:
|
72
|
+
raise UnsupportedPluginError("No Anydesk trace files found on target")
|
53
73
|
|
54
|
-
@export(record=
|
55
|
-
def logs(self):
|
56
|
-
"""
|
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)
|
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
|
66
|
-
|
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
|
-
|
72
|
-
if not line:
|
73
|
-
continue
|
74
|
-
|
75
|
-
if "* * * * * * * * * * * * * *" in line:
|
90
|
+
if not line or "* * * * * * * * * * * * * *" in line:
|
76
91
|
continue
|
77
92
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
6
|
-
"
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
15
|
-
"""
|
16
|
-
|
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
|
-
|
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
|
-
#
|
33
|
-
|
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,
|
51
|
+
self.logfiles.append([logfile, None])
|
37
52
|
|
38
|
-
#
|
53
|
+
# Find user log files.
|
39
54
|
for user_details in self.target.user_details.all_with_home():
|
40
|
-
for
|
41
|
-
|
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=
|
48
|
-
def logs(self):
|
49
|
-
"""
|
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
|
-
|
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,
|
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,
|
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
|
105
|
-
tool="teamviewer",
|
118
|
+
yield self.RemoteAccessLogRecord(
|
106
119
|
ts=timestamp,
|
107
|
-
|
108
|
-
|
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
|
-
|
83
|
-
|
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.
|
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=
|
132
|
-
dissect/target/plugins/apps/remoteaccess/remoteaccess.py,sha256=
|
133
|
-
dissect/target/plugins/apps/remoteaccess/teamviewer.py,sha256=
|
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=
|
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.
|
348
|
-
dissect.target-3.19.
|
349
|
-
dissect.target-3.19.
|
350
|
-
dissect.target-3.19.
|
351
|
-
dissect.target-3.19.
|
352
|
-
dissect.target-3.19.
|
353
|
-
dissect.target-3.19.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.19.dev13.dist-info → dissect.target-3.19.dev15.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|