diff --git a/dissect/target/plugins/apps/remoteaccess/teamviewer.py b/dissect/target/plugins/apps/remoteaccess/teamviewer.py index deb196223..538d9accc 100644 --- a/dissect/target/plugins/apps/remoteaccess/teamviewer.py +++ b/dissect/target/plugins/apps/remoteaccess/teamviewer.py @@ -1,3 +1,4 @@ +import re from datetime import datetime from dissect.target.exceptions import UnsupportedPluginError @@ -7,6 +8,8 @@ RemoteAccessRecord, ) +START_PATTERN = re.compile(r"^(\d{2}|\d{4})/") + class TeamviewerPlugin(RemoteAccessPlugin): """ @@ -54,30 +57,55 @@ def logs(self): for logfile, user in self.logfiles: logfile = self.target.fs.path(logfile) - for line in logfile.open("rt"): - line = line.strip() - - # Skip empty lines - if not line: - continue - - # Sometimes there are weird, mult-line/pretty print log messages. - try: - # should be year (%Y) - int(line[0]) - except ValueError: - continue - - ts_day, ts_time, description = line.split(" ", 2) - ts_time = ts_time.split(".")[0] - - timestamp = datetime.strptime(f"{ts_day} {ts_time}", "%Y/%m/%d %H:%M:%S") - - yield RemoteAccessRecord( - tool="teamviewer", - ts=timestamp, - logfile=str(logfile), - description=description, - _target=self.target, - _user=user, - ) + start_date = None + with logfile.open("rt") as file: + while True: + try: + line = file.readline() + except UnicodeDecodeError: + continue + + # End of file, quit while loop + if not line: + break + + line = line.strip() + + # Skip empty lines + if not line: + continue + # 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") + + yield RemoteAccessRecord( + tool="teamviewer", + ts=timestamp, + logfile=str(logfile), + description=description, + _target=self.target, + _user=user, + ) diff --git a/tests/_data/plugins/apps/remoteaccess/teamviewer/TestTeamviewer.log b/tests/_data/plugins/apps/remoteaccess/teamviewer/TestTeamviewer.log index 0f18a4022..28d2d6f4e 100644 --- a/tests/_data/plugins/apps/remoteaccess/teamviewer/TestTeamviewer.log +++ b/tests/_data/plugins/apps/remoteaccess/teamviewer/TestTeamviewer.log @@ -1,4 +1,13 @@ # skip # also skip as well as the blank line below +# "Start" is present in older logs, we can deduce the year from it +Start: 2021/11/11 12:34:56 + 2021/11/11 12:34:56 Strip the headers, trace the source! + +# Here is where the "Start" year is used to fill in the year +11/11 12:35:55.465 Should be year 2021 + +2021/11/11 12:36:11:111 Should discard the milliseconds properly +21/11/11 12:37:00.000 Should be year 2021 \ No newline at end of file diff --git a/tests/plugins/apps/remoteaccess/test_teamviewer.py b/tests/plugins/apps/remoteaccess/test_teamviewer.py index 4e17a6d80..640736397 100644 --- a/tests/plugins/apps/remoteaccess/test_teamviewer.py +++ b/tests/plugins/apps/remoteaccess/test_teamviewer.py @@ -14,7 +14,7 @@ def test_teamviewer_plugin_global_log(target_win_users, fs_win): tvp = TeamviewerPlugin(target_win_users) records = list(tvp.logs()) - assert len(records) == 1 + assert len(records) == 4 record = records[0] assert record.ts == datetime(2021, 11, 11, 12, 34, 56, tzinfo=timezone.utc) @@ -36,7 +36,7 @@ def test_teamviewer_plugin_user_log(target_win_users, fs_win): tvp = TeamviewerPlugin(target_win_users) records = list(tvp.logs()) - assert len(records) == 1 + assert len(records) == 4 record = records[0] assert record.ts == datetime(2021, 11, 11, 12, 34, 56, tzinfo=timezone.utc) @@ -45,3 +45,29 @@ def test_teamviewer_plugin_user_log(target_win_users, fs_win): assert record.username == user_details.user.name assert record.user_id == user_details.user.sid assert record.user_home == user_details.user.home + + +def test_teamviewer_plugin_special_date_parsing(target_win_users, fs_win): + teamviewer_logfile = absolute_path("_data/plugins/apps/remoteaccess/teamviewer/TestTeamviewer.log") + user_details = target_win_users.user_details.find(username="John") + target_logfile_name = f"{user_details.home_path}/appdata/roaming/teamviewer/teamviewer_TEST_logfile.log" + + _, _, map_path = target_logfile_name.partition("C:/") + fs_win.map_file(map_path, teamviewer_logfile) + + tvp = TeamviewerPlugin(target_win_users) + + records = list(tvp.logs()) + assert len(records) == 4 + + record_2 = records[1] + assert record_2.ts == datetime(2021, 11, 11, 12, 35, 55, tzinfo=timezone.utc) + assert record_2.description == "Should be year 2021" + + record_3 = records[2] + assert record_3.ts == datetime(2021, 11, 11, 12, 36, 11, tzinfo=timezone.utc) + assert record_3.description == "Should discard the milliseconds properly" + + record_4 = records[3] + assert record_4.ts == datetime(2021, 11, 11, 12, 37, 00, tzinfo=timezone.utc) + assert record_4.description == "Should be year 2021"