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

Add RustdeskPlugin Support #992

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

Peter-The-Great
Copy link

This PR adds support to inspect Rustdesk logs on supported targets (#983). I have tested the Plugin on a both a Linux and Windows machine and they do appear to work. However, if there is any issue with the plugin, just let me know right away and I will try to look in to it.

@Peter-The-Great Peter-The-Great marked this pull request as ready for review January 20, 2025 11:59
@EinatFox EinatFox linked an issue Jan 20, 2025 that may be closed by this pull request
@Peter-The-Great
Copy link
Author

IDK, what happened with the checks, but this is a complete disaster and i barely know what exactly went wrong. Although linting should make some sort of indication.



RemoteAccessLogRecord = create_extended_descriptor([UserRecordDescriptorExtension])(
"remoteaccess/restdesk/log", GENERIC_LOG_RECORD_FIELDS
Copy link

Choose a reason for hiding this comment

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

Typo here on the record descriptor, should be remoteaccess/rustdesk/log

@jd-gui
Copy link

jd-gui commented Feb 4, 2025

@Peter-The-Great Should also use pep8 for formatting as the linter is expecting blank lines and some lines are too long. Have reformatted and attached the two code files here if you like
reformatted.zip

@Peter-The-Great
Copy link
Author

Alright i have just added the PEP 8 code Style to the code and fixed the typo.

@@ -0,0 +1,115 @@
import logging
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import logging
from __future__ import annotations

Comment on lines +17 to +18

log = logging.getLogger(__name__)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
log = logging.getLogger(__name__)

SERVER_GLOBS = [
# Windows >= Windows 7
"sysvol/Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/*.log",

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

Comment on lines +39 to +50
USER_GLOBS = [
# Windows
"AppData/Roaming/Rustdesk/log/*.log",

# Linux
".local/share/logs/RustDesk/server/*.log",

# Android
"storage/emulated/0/RustDesk/logs/*.log",

# Mac
"Library/Logs/RustDesk/*.log",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
USER_GLOBS = [
# Windows
"AppData/Roaming/Rustdesk/log/*.log",
# Linux
".local/share/logs/RustDesk/server/*.log",
# Android
"storage/emulated/0/RustDesk/logs/*.log",
# Mac
"Library/Logs/RustDesk/*.log",
USER_GLOBS = [
# Windows
"AppData/Roaming/Rustdesk/log/*.log",
# Linux
".local/share/logs/RustDesk/server/*.log",
# Android
"storage/emulated/0/RustDesk/logs/*.log",
# Mac
"Library/Logs/RustDesk/*.log",


assert records[0].ts == datetime(2025, 1, 1, 13, 4, 8, 350802, tzinfo=timezone.utc)
assert records[0].message == "DEBUG src\\server\\connection.rs:983 #1362 Connection opened from REDACTED IP:6074."
assert records[0].source == "sysvol/Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/TestRustdesk.log"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert records[0].source == "sysvol/Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/TestRustdesk.log"
assert (
records[0].source
== "sysvol/Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/TestRustdesk.log"
)

Comment on lines +59 to +62
user = None
for log_glob in self.SERVER_GLOBS:
for log_file in self.target.fs.path().glob(log_glob):
self.log_files.add((log_file, user))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
user = None
for log_glob in self.SERVER_GLOBS:
for log_file in self.target.fs.path().glob(log_glob):
self.log_files.add((log_file, user))
for log_glob in self.SERVER_GLOBS:
for log_file in self.target.fs.path().glob(log_glob):
self.log_files.add((log_file, None))

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

self.log_files: set[tuple[TargetPath, UserDetails]] = set()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
self.log_files: set[tuple[TargetPath, UserDetails]] = set()
self.log_files: set[tuple[TargetPath, UserDetails | None]] = set()


def test_rustdesk_plugin_log(target_win_users: Target, fs_win: VirtualFilesystem) -> None:
fs_win.map_file(
"sysvol/Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/TestRustdesk.log",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"sysvol/Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/TestRustdesk.log",
"Windows/ServiceProfiles/LocalService/AppData/Roaming/RustDesk/log/server/TestRustdesk.log",


ts, level, source, message = match.groups()

timestamp = datetime.fromisoformat(ts)
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like the ts parsed from the log file is not a valid ISO format timestamp because of the space between the timestamp and +01:00. This causes the tests to fail currently.

Comment on lines +92 to +115
line = line.strip()

try:
# Still needs to be checked for Rustdesk implementation
match = re.match(r"\[(.*?)\] (\w+) \[(.*?)\] (.*)", line)
if not match:
raise ValueError("Line does not match expected format")

ts, level, source, message = match.groups()

timestamp = datetime.fromisoformat(ts)
message = re.sub(r"\s\s+", " ", f"{level} {source} {message}")

yield self.RemoteAccessLogRecord(
ts=timestamp,
message=message,
source=log_file,
_target=self.target,
_user=user,
)

except ValueError as e:
self.target.log.warning("Could not parse log line in file %s: '%s'", log_file, line)
self.target.log.debug("", exc_info=e)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
line = line.strip()
try:
# Still needs to be checked for Rustdesk implementation
match = re.match(r"\[(.*?)\] (\w+) \[(.*?)\] (.*)", line)
if not match:
raise ValueError("Line does not match expected format")
ts, level, source, message = match.groups()
timestamp = datetime.fromisoformat(ts)
message = re.sub(r"\s\s+", " ", f"{level} {source} {message}")
yield self.RemoteAccessLogRecord(
ts=timestamp,
message=message,
source=log_file,
_target=self.target,
_user=user,
)
except ValueError as e:
self.target.log.warning("Could not parse log line in file %s: '%s'", log_file, line)
self.target.log.debug("", exc_info=e)
if line := line.strip():
try:
# Still needs to be checked for Rustdesk implementation
match = re.match(r"\[(.*?)\] (\w+) \[(.*?)\] (.*)", line)
if not match:
raise ValueError("Line does not match expected format")
ts, level, source, message = match.groups()
timestamp = datetime.fromisoformat(ts)
message = re.sub(r"\s\s+", " ", f"{level} {source} {message}")
yield self.RemoteAccessLogRecord(
ts=timestamp,
message=message,
source=log_file,
_target=self.target,
_user=user,
)
except ValueError as e:
self.target.log.warning("Could not parse log line in file %s: '%s'", log_file, line)
self.target.log.debug("", exc_info=e)


@export(record=RemoteAccessLogRecord)
def logs(self) -> Iterator[RemoteAccessLogRecord]:
"""Parse Rustdesk log files.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"""Parse Rustdesk log files.
"""Parse RustDesk log files.

def logs(self) -> Iterator[RemoteAccessLogRecord]:
"""Parse Rustdesk log files.

Rustdesk is remote access software that allows users to connect to a remote computer.
Copy link
Contributor

@Horofic Horofic Feb 11, 2025

Choose a reason for hiding this comment

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

Suggested change
Rustdesk is remote access software that allows users to connect to a remote computer.
RustDesk is a remote desktop application can be used by adversaries to get (persistent) access to a machine.

The project is open source and can be found at: https://github.com/rustdesk/rustdesk/

The log files are stored in different locations, based on the Target OS and client type.
Unlike Anydesk, Rustdesk does have a carry a time zone designator (TZD).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Unlike Anydesk, Rustdesk does have a carry a time zone designator (TZD).
Unlike AnyDesk, RustDesk does carry a time zone designator (TZD).

@Horofic Horofic self-assigned this Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create RustDesk plugin
3 participants