Skip to content

Commit a266fc5

Browse files
R3dP1llSchamper
andauthored
Plugin for teamviewer incoming connections (#701)
Co-authored-by: Schamper <[email protected]>
1 parent 4e9381b commit a266fc5

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

dissect/target/plugins/apps/remoteaccess/teamviewer.py

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from __future__ import annotations
22

33
import re
4-
from datetime import datetime
4+
from datetime import datetime, timezone
55
from typing import TYPE_CHECKING
66

77
from dissect.target.exceptions import UnsupportedPluginError
88
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
9-
from dissect.target.helpers.record import create_extended_descriptor
9+
from dissect.target.helpers.record import TargetRecordDescriptor, create_extended_descriptor
1010
from dissect.target.plugin import export
1111
from dissect.target.plugins.apps.remoteaccess.remoteaccess import (
1212
GENERIC_LOG_RECORD_FIELDS,
@@ -49,6 +49,20 @@
4949
)
5050

5151

52+
TeamviewerIncomingRecord = TargetRecordDescriptor(
53+
"remoteaccess/teamviewer/incoming",
54+
[
55+
("datetime", "ts"),
56+
("datetime", "end"),
57+
("string", "remote_id"),
58+
("string", "name"),
59+
("string", "user"),
60+
("string", "connection_type"),
61+
("string", "connection_id"),
62+
],
63+
)
64+
65+
5266
class TeamViewerPlugin(RemoteAccessPlugin):
5367
"""TeamViewer client plugin.
5468
@@ -66,6 +80,11 @@ class TeamViewerPlugin(RemoteAccessPlugin):
6680
"/var/log/teamviewer*/*.log",
6781
)
6882

83+
SYSTEM_INCOMING_GLOBS = (
84+
"sysvol/Program Files/TeamViewer/*_incoming.txt",
85+
"sysvol/Program Files (x86)/TeamViewer/*_incoming.txt",
86+
)
87+
6988
USER_GLOBS = (
7089
"AppData/Roaming/TeamViewer/teamviewer*_logfile.log",
7190
"Library/Logs/TeamViewer/teamviewer*_logfile*.log",
@@ -79,20 +98,26 @@ def __init__(self, target: Target):
7998
super().__init__(target)
8099

81100
self.logfiles: set[tuple[str, UserDetails | None]] = set()
101+
self.incoming_logfiles: set[str] = set()
82102

83103
# Find system service log files.
84104
for log_glob in self.SYSTEM_GLOBS:
85105
for logfile in self.target.fs.glob(log_glob):
86106
self.logfiles.add((logfile, None))
87107

108+
# Find system incoming connection log files.
109+
for log_glob in self.SYSTEM_INCOMING_GLOBS:
110+
for logfile in self.target.fs.glob(log_glob):
111+
self.incoming_logfiles.add(logfile)
112+
88113
# Find user log files.
89114
for user_details in self.target.user_details.all_with_home():
90115
for log_glob in self.USER_GLOBS:
91116
for logfile in user_details.home_path.glob(log_glob):
92117
self.logfiles.add((logfile, user_details))
93118

94119
def check_compatible(self) -> None:
95-
if not len(self.logfiles):
120+
if not len(self.logfiles) and not len(self.incoming_logfiles):
96121
raise UnsupportedPluginError("No Teamviewer logs found on target")
97122

98123
@export(record=RemoteAccessLogRecord)
@@ -169,6 +194,51 @@ def logs(self) -> Iterator[RemoteAccessLogRecord]:
169194
_user=user_details.user if user_details else None,
170195
)
171196

197+
@export(record=TeamviewerIncomingRecord)
198+
def incoming(self) -> Iterator[TeamviewerIncomingRecord]:
199+
"""Yield TeamViewer incoming connection logs.
200+
201+
TeamViewer is a commercial remote desktop application. An adversary may use it to gain persistence on a system.
202+
"""
203+
for logfile in self.incoming_logfiles:
204+
logfile = self.target.fs.path(logfile)
205+
206+
for line in logfile.open("rt", errors="replace"):
207+
if not (line := line.strip()) or line.startswith("# "):
208+
continue
209+
210+
fields = line.split("\t")
211+
if len(fields) < 7:
212+
self.target.log.warning("Skipping TeamViewer incoming connection log line %r in %s", line, logfile)
213+
continue
214+
215+
try:
216+
start = datetime.strptime(fields[2], "%d-%m-%Y %H:%M:%S").replace(tzinfo=timezone.utc)
217+
end = datetime.strptime(fields[3], "%d-%m-%Y %H:%M:%S").replace(tzinfo=timezone.utc)
218+
except Exception as e:
219+
self.target.log.warning(
220+
"Unable to parse timestamps in TeamViewer incoming connection log line %r in %s", line, logfile
221+
)
222+
self.target.log.debug("", exc_info=e)
223+
continue
224+
225+
remote_id = fields[0]
226+
name = fields[1]
227+
user = fields[4]
228+
connection_type = fields[5]
229+
connection_id = fields[6]
230+
231+
yield TeamviewerIncomingRecord(
232+
ts=start,
233+
end=end,
234+
remote_id=remote_id,
235+
name=name,
236+
user=user,
237+
connection_type=connection_type,
238+
connection_id=connection_id,
239+
_target=self.target,
240+
)
241+
172242

173243
def parse_start(line: str) -> datetime | None:
174244
"""TeamViewer ``Start`` messages can be formatted in different ways

tests/plugins/apps/remoteaccess/test_teamviewer.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,33 @@ def test_teamviewer_timezone(target_win_users: Target, fs_win: VirtualFilesystem
106106
assert records[0].message == "1234 5678 G1 LanguageControl: device language is 'enUS'"
107107
assert records[0].source == "C:\\Users\\John\\AppData\\Roaming\\TeamViewer\\TeamViewer1337_Logfile.log"
108108
assert records[0].username == "John"
109+
110+
111+
def test_teamviewer_incoming(target_win_users: Target, fs_win: VirtualFilesystem) -> None:
112+
"""Test TeamViewer incoming connection log parsing."""
113+
log = """
114+
1031857653 DESKTOP-CAK7OMO 11-09-2022 14:44:03 11-09-2022 15:27:53 SERVER TV RemoteControl {C2CC2F16-D1F4-4547-9928-EE63891D4CC0}
115+
1031857653 DESKTOP-CAK7OMO 22-12-2022 19:25:22 22-12-2022 19:49:28 Server RemoteControl {4BF22BA7-32BA-4F64-8755-97E6E45F9883}
116+
""" # noqa: E501
117+
fs_win.map_file_fh("Program Files/TeamViewer/Connections_incoming.txt", BytesIO(dedent(log).encode()))
118+
119+
target_win_users.add_plugin(TeamViewerPlugin)
120+
121+
records = list(target_win_users.teamviewer.incoming())
122+
assert len(records) == 2
123+
124+
assert records[0].ts == datetime(2022, 9, 11, 14, 44, 3, tzinfo=timezone.utc)
125+
assert records[0].end == datetime(2022, 9, 11, 15, 27, 53, tzinfo=timezone.utc)
126+
assert records[0].remote_id == "1031857653"
127+
assert records[0].name == "DESKTOP-CAK7OMO"
128+
assert records[0].user == "SERVER TV"
129+
assert records[0].connection_type == "RemoteControl"
130+
assert records[0].connection_id == "{C2CC2F16-D1F4-4547-9928-EE63891D4CC0}"
131+
132+
assert records[1].ts == datetime(2022, 12, 22, 19, 25, 22, tzinfo=timezone.utc)
133+
assert records[1].end == datetime(2022, 12, 22, 19, 49, 28, tzinfo=timezone.utc)
134+
assert records[1].remote_id == "1031857653"
135+
assert records[1].name == "DESKTOP-CAK7OMO"
136+
assert records[1].user == "Server"
137+
assert records[1].connection_type == "RemoteControl"
138+
assert records[1].connection_id == "{4BF22BA7-32BA-4F64-8755-97E6E45F9883}"

0 commit comments

Comments
 (0)