Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin for teamviewer incoming connections #701

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion dissect/target/plugins/apps/remoteaccess/remoteaccess.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dissect.target.helpers.descriptor_extensions import UserRecordDescriptorExtension
from dissect.target.helpers.record import create_extended_descriptor
from dissect.target.plugin import NamespacePlugin
from dissect.target.helpers.record import TargetRecordDescriptor

RemoteAccessRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
"application/log/remoteaccess",
Expand All @@ -11,7 +12,23 @@
("string", "description"),
],
)

RemoteAccessIncomingConnectionRecord = TargetRecordDescriptor(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems pretty specific to TeamViewer right now, so perhaps just place this in teamviewer.py and call it TeamViewerIncomingConnectionRecord.

Could you also put the start_time and end_time at the top?

"application/log/remoteaccess",
[

("string", "tool"),
("path", "logfile"),
("string", "remote_tvid"),
("string", "tv_user_host"),
("string", "tv_user_host"),
("datetime", "start_time"),
#("string","host"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed?

("datetime", "end_time"),
("string", "user_context"),
("string", "connection_type"),
("string", "connection_guid"),
],
)

class RemoteAccessPlugin(NamespacePlugin):
"""General Remote Access plugin.
Expand Down
111 changes: 108 additions & 3 deletions dissect/target/plugins/apps/remoteaccess/teamviewer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import re
from datetime import datetime

#import datetime
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#import datetime

from dissect.target.exceptions import UnsupportedPluginError
from dissect.target.plugin import export
from dissect.target.plugins.apps.remoteaccess.remoteaccess import (
RemoteAccessPlugin,
RemoteAccessRecord,
RemoteAccessIncomingConnectionRecord
)

START_PATTERN = re.compile(r"^(\d{2}|\d{4})/")
Expand All @@ -23,25 +24,34 @@ class TeamviewerPlugin(RemoteAccessPlugin):
"sysvol/Program Files/TeamViewer/*.log",
"sysvol/Program Files (x86)/TeamViewer/*.log",
]
INCOMING_GLOBS = [
"sysvol/Program Files/TeamViewer/*_incoming.txt",
"sysvol/Program Files (x86)/TeamViewer/*_incoming.txt",
]

def __init__(self, target):
super().__init__(target)

self.logfiles = []

self.incoming_logfiles = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.incoming_logfiles = []
self.incoming_logfiles = []

# Check service globs
user = None
for log_glob in self.GLOBS:
for logfile in self.target.fs.glob(log_glob):
self.logfiles.append([logfile, user])

for log_glob in self.INCOMING_GLOBS:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

for logfile in self.target.fs.glob(log_glob):
self.incoming_logfiles.append(logfile)

# Teamviewer logs when as user (Windows)
for user_details in self.target.user_details.all_with_home():
for logfile in user_details.home_path.glob("appdata/roaming/teamviewer/teamviewer*_logfile.log"):
self.logfiles.append([logfile, user_details.user])

def check_compatible(self) -> None:
if not len(self.logfiles):
if not len(self.logfiles) and not len(self.incoming_logfiles):
raise UnsupportedPluginError("No Teamviewer logs found")

@export(record=RemoteAccessRecord)
Expand Down Expand Up @@ -109,3 +119,98 @@ def logs(self):
_target=self.target,
_user=user,
)

@export(record=RemoteAccessIncomingConnectionRecord)
def incoming_connections(self):
"""Return the content of the TeamViewer incoming connections logs.

TeamViewer is a commercial remote desktop application. An adversary may use it to gain persistence on a
system.

References:
- https://www.teamviewer.com/nl/
"""
hostname=str(self.target).split("Collection-")[1].split("-")[0]
for logfile in self.incoming_logfiles:

logfile = self.target.fs.path(logfile)

with logfile.open("rt",encoding='latin-1') as file:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
with logfile.open("rt",encoding='latin-1') as file:
with logfile.open("rt") as file:

Does this not work?

next(file)
while True:
try:
line = file.readline()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

except UnicodeDecodeError:
continue

# End of file, quit while loop
if not line:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

break

line = line.strip()

# Skip empty lines
if not line:
continue

fields = line.split('\t')
if len(fields) < 7:
print("Line does not contain enough fields:", line)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
print("Line does not contain enough fields:", line)
self.target.log.debug("Line does not contain enough fields: %s", line)

continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
continue
continue

remote_teamviewer_id = fields[0]
username_or_hostname = fields[1]
#print(username_or_hostname)
starttime = datetime.strptime(fields[2], '%d-%m-%Y %H:%M:%S') #.strftime('%Y-%m-%d %H:%M:%S')
endtime = datetime.strptime(fields[3], '%d-%m-%Y %H:%M:%S') #.strftime('%Y/%m/%d %H:%M:%S')
connected_user = fields[4]
connection_type = fields[5]
connection_guid = fields[6].strip() # Remove any trailing whitespace
'''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still relevant? Can you remove any commented code?

# Older logs first mention the start time and then leave out the year
if line.startswith("Start:"):
start_date = datetime.strptime(line.split()[1], "%Y/%m/%d")

# Sometimes there are weird, mult-line/pretty print log messages.
# We only parse the start line which starts with year (%Y/) or month (%m/)
if not re.match(START_PATTERN, line):
continue

ts_day, ts_time, description = line.split(" ", 2)
ts_time = ts_time.split(".")[0]

# Correct for use of : as millisecond separator
if ts_time.count(":") > 2:
ts_time = ":".join(ts_time.split(":")[:3])
# Correct for missing year in date
if ts_day.count("/") == 1:
if not start_date:
self.target.log.debug("Missing year in log line, skipping line.")
continue
ts_day = f"{start_date.year}/{ts_day}"
# Correct for year if short notation for 2000 is used
if ts_day.count("/") == 2 and len(ts_day.split("/")[0]) == 2:
ts_day = "20" + ts_day

timestamp = datetime.strptime(f"{ts_day} {ts_time}", "%Y/%m/%d %H:%M:%S")


'''
#print(starttime)
#print(endtime)
yield RemoteAccessIncomingConnectionRecord(
tool="teamviewer",
logfile=str(logfile),
remote_tvid=remote_teamviewer_id,
tv_user_host=username_or_hostname,
start_time=starttime,
end_time=endtime,
user_context=connected_user,
connection_type=connection_type,
connection_guid=connection_guid,

_target=self.target,

)