From b02fff7e3ca1dac8454a46bfe88c29e330400b8e Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 16:04:44 -0500 Subject: [PATCH 01/11] Add handling for missing or wrong key file password --- nxc/protocols/ssh.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 7c4716a7e..eefa6b7c9 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -187,7 +187,6 @@ def check_if_admin_sudo(self): def plaintext_login(self, username, password, private_key=""): self.username = username self.password = password - stdout = None try: if self.args.key_file or private_key: self.logger.debug(f"Logging {self.host} with username: {username}, keyfile: {self.args.key_file}") @@ -228,12 +227,15 @@ def plaintext_login(self, username, password, private_key=""): # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey _, stdout, _ = self.conn.exec_command("id") stdout = stdout.read().decode(self.args.codec, errors="ignore") - except AuthenticationException: - self.logger.fail(f"{username}:{process_secret(password)}") + except AuthenticationException as e: + if "Private key file is encrypted" in str(e): + self.logger.fail(f"{username}:{process_secret(password)} Could not load private key, error: {e}") + else: + self.logger.fail(f"{username}:{process_secret(password)}") except SSHException as e: if "Invalid key" in str(e): - self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, error: {e}") - if "Error reading SSH protocol banner" in str(e): + self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, invalid password") + elif "Error reading SSH protocol banner" in str(e): self.logger.error(f"Internal Paramiko error for {username}:{process_secret(password)}, {e}") else: self.logger.exception(e) From 176b9618f13b9944d2bf6b93f4c32820ed048f46 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 16:13:16 -0500 Subject: [PATCH 02/11] Move plaintext_login above priv checks --- nxc/protocols/ssh.py | 198 +++++++++++++++++++++---------------------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index eefa6b7c9..97e0858ed 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -77,6 +77,105 @@ def create_conn_obj(self): except OSError: return False + def plaintext_login(self, username, password, private_key=""): + self.username = username + self.password = password + try: + if self.args.key_file or private_key: + self.logger.debug(f"Logging {self.host} with username: {username}, keyfile: {self.args.key_file}") + + self.conn.connect( + self.host, + port=self.port, + username=username, + passphrase=password if password != "" else None, + key_filename=private_key if private_key else self.args.key_file, + timeout=self.args.ssh_timeout, + look_for_keys=False, + allow_agent=False, + banner_timeout=self.args.ssh_timeout, + ) + + cred_id = self.db.add_credential( + "key", + username, + password if password != "" else "", + key=private_key, + ) + + else: + self.logger.debug(f"Logging {self.host} with username: {self.username}, password: {self.password}") + self.conn.connect( + self.host, + port=self.port, + username=username, + password=password, + timeout=self.args.ssh_timeout, + look_for_keys=False, + allow_agent=False, + banner_timeout=self.args.ssh_timeout, + ) + cred_id = self.db.add_credential("plaintext", username, password) + + # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey + _, stdout, _ = self.conn.exec_command("id") + stdout = stdout.read().decode(self.args.codec, errors="ignore") + except AuthenticationException as e: + if "Private key file is encrypted" in str(e): + self.logger.fail(f"{username}:{process_secret(password)} Could not load private key, error: {e}") + else: + self.logger.fail(f"{username}:{process_secret(password)}") + except SSHException as e: + if "Invalid key" in str(e): + self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, invalid password") + elif "Error reading SSH protocol banner" in str(e): + self.logger.error(f"Internal Paramiko error for {username}:{process_secret(password)}, {e}") + else: + self.logger.exception(e) + except Exception as e: + self.logger.exception(e) + self.conn.close() + return False + else: + shell_access = False + host_id = self.db.get_hosts(self.host)[0].id + + if not stdout: + _, stdout, _ = self.conn.exec_command("whoami /priv") + stdout = stdout.read().decode(self.args.codec, errors="ignore") + self.server_os_platform = "Windows" + if "SeDebugPrivilege" in stdout: + self.admin_privs = True + elif "SeUndockPrivilege" in stdout: + self.admin_privs = True + self.uac = "with UAC - " + + if not stdout: + self.logger.debug(f"User: {self.username} can't get a basic shell") + self.server_os_platform = "Network Devices" + shell_access = False + else: + shell_access = True + + self.db.add_loggedin_relation(cred_id, host_id, shell=shell_access) + + if shell_access and self.server_os_platform == "Linux": + self.check_if_admin() + if self.admin_privs: + self.logger.debug(f"User {username} logged in successfully and is root!") + if self.args.key_file: + self.db.add_admin_user("key", username, password, host_id=host_id, cred_id=cred_id) + else: + self.db.add_admin_user("plaintext", username, password, host_id=host_id, cred_id=cred_id) + + if self.args.key_file: + password = f"{process_secret(password)} (keyfile: {self.args.key_file})" + + display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if shell_access else ''}" + self.logger.success(f"{username}:{process_secret(password)} {self.mark_pwned()} {highlight(display_shell_access)}") + + return True + def check_if_admin(self): self.admin_privs = False @@ -184,105 +283,6 @@ def check_if_admin_sudo(self): self.logger.error("Command: 'mkfifo' unavailable, running command with 'sudo' failed") return - def plaintext_login(self, username, password, private_key=""): - self.username = username - self.password = password - try: - if self.args.key_file or private_key: - self.logger.debug(f"Logging {self.host} with username: {username}, keyfile: {self.args.key_file}") - - self.conn.connect( - self.host, - port=self.port, - username=username, - passphrase=password if password != "" else None, - key_filename=private_key if private_key else self.args.key_file, - timeout=self.args.ssh_timeout, - look_for_keys=False, - allow_agent=False, - banner_timeout=self.args.ssh_timeout, - ) - - cred_id = self.db.add_credential( - "key", - username, - password if password != "" else "", - key=private_key, - ) - - else: - self.logger.debug(f"Logging {self.host} with username: {self.username}, password: {self.password}") - self.conn.connect( - self.host, - port=self.port, - username=username, - password=password, - timeout=self.args.ssh_timeout, - look_for_keys=False, - allow_agent=False, - banner_timeout=self.args.ssh_timeout, - ) - cred_id = self.db.add_credential("plaintext", username, password) - - # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey - _, stdout, _ = self.conn.exec_command("id") - stdout = stdout.read().decode(self.args.codec, errors="ignore") - except AuthenticationException as e: - if "Private key file is encrypted" in str(e): - self.logger.fail(f"{username}:{process_secret(password)} Could not load private key, error: {e}") - else: - self.logger.fail(f"{username}:{process_secret(password)}") - except SSHException as e: - if "Invalid key" in str(e): - self.logger.fail(f"{username}:{process_secret(password)} Could not decrypt private key, invalid password") - elif "Error reading SSH protocol banner" in str(e): - self.logger.error(f"Internal Paramiko error for {username}:{process_secret(password)}, {e}") - else: - self.logger.exception(e) - except Exception as e: - self.logger.exception(e) - self.conn.close() - return False - else: - shell_access = False - host_id = self.db.get_hosts(self.host)[0].id - - if not stdout: - _, stdout, _ = self.conn.exec_command("whoami /priv") - stdout = stdout.read().decode(self.args.codec, errors="ignore") - self.server_os_platform = "Windows" - if "SeDebugPrivilege" in stdout: - self.admin_privs = True - elif "SeUndockPrivilege" in stdout: - self.admin_privs = True - self.uac = "with UAC - " - - if not stdout: - self.logger.debug(f"User: {self.username} can't get a basic shell") - self.server_os_platform = "Network Devices" - shell_access = False - else: - shell_access = True - - self.db.add_loggedin_relation(cred_id, host_id, shell=shell_access) - - if shell_access and self.server_os_platform == "Linux": - self.check_if_admin() - if self.admin_privs: - self.logger.debug(f"User {username} logged in successfully and is root!") - if self.args.key_file: - self.db.add_admin_user("key", username, password, host_id=host_id, cred_id=cred_id) - else: - self.db.add_admin_user("plaintext", username, password, host_id=host_id, cred_id=cred_id) - - if self.args.key_file: - password = f"{process_secret(password)} (keyfile: {self.args.key_file})" - - display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if shell_access else ''}" - self.logger.success(f"{username}:{process_secret(password)} {self.mark_pwned()} {highlight(display_shell_access)}") - - return True - def put_file_single(self, sftp_conn, src, dst): self.logger.display(f'Copying "{src}" to "{dst}"') try: From 07540ee0c7be3b818b5fb86c55f0834e34ab1c91 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 16:13:59 -0500 Subject: [PATCH 03/11] Renanem priv functions to reflect its implementation --- nxc/protocols/ssh.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 97e0858ed..177b40849 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -160,7 +160,7 @@ def plaintext_login(self, username, password, private_key=""): self.db.add_loggedin_relation(cred_id, host_id, shell=shell_access) if shell_access and self.server_os_platform == "Linux": - self.check_if_admin() + self.check_linux_priv() if self.admin_privs: self.logger.debug(f"User {username} logged in successfully and is root!") if self.args.key_file: @@ -176,11 +176,11 @@ def plaintext_login(self, username, password, private_key=""): return True - def check_if_admin(self): + def check_linux_priv(self): self.admin_privs = False if self.args.sudo_check: - self.check_if_admin_sudo() + self.check_linux_priv_sudo() return # we could add in another method to check by piping in the password to sudo @@ -207,7 +207,7 @@ def check_if_admin(self): self.logger.display(tips) return - def check_if_admin_sudo(self): + def check_linux_priv_sudo(self): if not self.password: self.logger.error("Check admin with sudo does not support using a private key") return From 6593fa84db400f4cc7b49217487cc950795e44cd Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 16:37:17 -0500 Subject: [PATCH 04/11] Extracting privilege checks from plaintext_login --- nxc/protocols/ssh.py | 71 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 177b40849..739e2e134 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -102,7 +102,6 @@ def plaintext_login(self, username, password, private_key=""): password if password != "" else "", key=private_key, ) - else: self.logger.debug(f"Logging {self.host} with username: {self.username}, password: {self.password}") self.conn.connect( @@ -118,8 +117,12 @@ def plaintext_login(self, username, password, private_key=""): cred_id = self.db.add_credential("plaintext", username, password) # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey + # Also an early check if we are on Linux or not, as on windows only stderr and not stdout is returned ("id" is not implemented) _, stdout, _ = self.conn.exec_command("id") stdout = stdout.read().decode(self.args.codec, errors="ignore") + + self.check_privs(cred_id, stdout) + return True except AuthenticationException as e: if "Private key file is encrypted" in str(e): self.logger.fail(f"{username}:{process_secret(password)} Could not load private key, error: {e}") @@ -136,45 +139,43 @@ def plaintext_login(self, username, password, private_key=""): self.logger.exception(e) self.conn.close() return False - else: - shell_access = False - host_id = self.db.get_hosts(self.host)[0].id - - if not stdout: - _, stdout, _ = self.conn.exec_command("whoami /priv") - stdout = stdout.read().decode(self.args.codec, errors="ignore") - self.server_os_platform = "Windows" - if "SeDebugPrivilege" in stdout: - self.admin_privs = True - elif "SeUndockPrivilege" in stdout: - self.admin_privs = True - self.uac = "with UAC - " - - if not stdout: - self.logger.debug(f"User: {self.username} can't get a basic shell") - self.server_os_platform = "Network Devices" - shell_access = False - else: - shell_access = True - self.db.add_loggedin_relation(cred_id, host_id, shell=shell_access) + def check_privs(self, cred_id, stdout): + shell_access = False + host_id = self.db.get_hosts(self.host)[0].id - if shell_access and self.server_os_platform == "Linux": - self.check_linux_priv() - if self.admin_privs: - self.logger.debug(f"User {username} logged in successfully and is root!") - if self.args.key_file: - self.db.add_admin_user("key", username, password, host_id=host_id, cred_id=cred_id) - else: - self.db.add_admin_user("plaintext", username, password, host_id=host_id, cred_id=cred_id) + # If we have stdout we know it must be linux, "id" is not implemented on Windows + if not stdout: + self.server_os_platform = "Windows" + _, stdout, _ = self.conn.exec_command("whoami /priv") + stdout = stdout.read().decode(self.args.codec, errors="ignore") + if "SeDebugPrivilege" in stdout: + self.admin_privs = True + elif "SeUndockPrivilege" in stdout: + self.admin_privs = True + self.uac = "with UAC - " + + if not stdout: + self.logger.debug(f"User: {self.username} can't get a basic shell") + self.server_os_platform = "Network Devices" + shell_access = False + else: + shell_access = True - if self.args.key_file: - password = f"{process_secret(password)} (keyfile: {self.args.key_file})" + self.db.add_loggedin_relation(cred_id, host_id, shell=shell_access) - display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if shell_access else ''}" - self.logger.success(f"{username}:{process_secret(password)} {self.mark_pwned()} {highlight(display_shell_access)}") + if shell_access and self.server_os_platform == "Linux": + self.check_linux_priv() + if self.admin_privs: + self.logger.debug(f"User {self.username} logged in successfully and is root!") + if self.args.key_file: + self.db.add_admin_user("key", self.username, self.password, host_id=host_id, cred_id=cred_id) + else: + self.db.add_admin_user("plaintext", self.username, self.password, host_id=host_id, cred_id=cred_id) - return True + out = process_secret(self.password) if not self.args.key_file else f"{process_secret(self.password)} (keyfile: {self.args.key_file})" + display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if shell_access else ''}" + self.logger.success(f"{self.username}:{process_secret(out)} {self.mark_pwned()} {highlight(display_shell_access)}") def check_linux_priv(self): self.admin_privs = False From cfc8f2fcfa661b329d29b111562a2f44578b4e47 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 16:37:57 -0500 Subject: [PATCH 05/11] Fix auth --- nxc/protocols/ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 739e2e134..7f9d3fd08 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -138,7 +138,7 @@ def plaintext_login(self, username, password, private_key=""): except Exception as e: self.logger.exception(e) self.conn.close() - return False + return False def check_privs(self, cred_id, stdout): shell_access = False From caba9f662f94309c59ac5050d2409e21f3802862 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 17:42:31 -0500 Subject: [PATCH 06/11] Refactor privilege check logic --- nxc/protocols/ssh.py | 68 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 7f9d3fd08..f2a7c35c3 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -20,6 +20,8 @@ def __init__(self, args, db, host): self.protocol = "SSH" self.remote_version = "Unknown SSH Version" self.server_os_platform = "Linux" + self.shell_access = False + self.admin_privs = False self.uac = "" super().__init__(args, db, host) @@ -116,12 +118,11 @@ def plaintext_login(self, username, password, private_key=""): ) cred_id = self.db.add_credential("plaintext", username, password) - # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey - # Also an early check if we are on Linux or not, as on windows only stderr and not stdout is returned ("id" is not implemented) - _, stdout, _ = self.conn.exec_command("id") - stdout = stdout.read().decode(self.args.codec, errors="ignore") + self.check_shell(cred_id) - self.check_privs(cred_id, stdout) + out = process_secret(self.password) if not self.args.key_file else f"{process_secret(self.password)} (keyfile: {self.args.key_file})" + display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if self.shell_access else ''}" + self.logger.success(f"{self.username}:{process_secret(out)} {self.mark_pwned()} {highlight(display_shell_access)}") return True except AuthenticationException as e: if "Private key file is encrypted" in str(e): @@ -140,31 +141,16 @@ def plaintext_login(self, username, password, private_key=""): self.conn.close() return False - def check_privs(self, cred_id, stdout): - shell_access = False + def check_shell(self, cred_id): host_id = self.db.get_hosts(self.host)[0].id - # If we have stdout we know it must be linux, "id" is not implemented on Windows - if not stdout: - self.server_os_platform = "Windows" - _, stdout, _ = self.conn.exec_command("whoami /priv") - stdout = stdout.read().decode(self.args.codec, errors="ignore") - if "SeDebugPrivilege" in stdout: - self.admin_privs = True - elif "SeUndockPrivilege" in stdout: - self.admin_privs = True - self.uac = "with UAC - " - - if not stdout: - self.logger.debug(f"User: {self.username} can't get a basic shell") - self.server_os_platform = "Network Devices" - shell_access = False - else: - shell_access = True - - self.db.add_loggedin_relation(cred_id, host_id, shell=shell_access) - - if shell_access and self.server_os_platform == "Linux": + # Some IOT devices will not raise exception in self.conn._transport.auth_password / self.conn._transport.auth_publickey + # Check Linux + stdout = self.conn.exec_command("id")[1].read().decode(self.args.codec, errors="ignore") + if stdout: + self.server_os_platform = "Linux" + self.logger.debug(f"Linux detected for user: {stdout}") + self.shell_access = True self.check_linux_priv() if self.admin_privs: self.logger.debug(f"User {self.username} logged in successfully and is root!") @@ -172,10 +158,30 @@ def check_privs(self, cred_id, stdout): self.db.add_admin_user("key", self.username, self.password, host_id=host_id, cred_id=cred_id) else: self.db.add_admin_user("plaintext", self.username, self.password, host_id=host_id, cred_id=cred_id) + return + + # Check Windows + stdout = self.conn.exec_command("whoami /priv")[1].read().decode(self.args.codec, errors="ignore") + if stdout: + self.server_os_platform = "Windows" + self.logger.debug(f"Windows detected for user: {stdout}") + self.shell_access = True + self.check_windows_priv(stdout) + self.db.add_loggedin_relation(cred_id, host_id, shell=self.shell_access) + return + + # No shell access + self.shell_access = False + self.logger.debug(f"User: {self.username} can't get a basic shell") + self.server_os_platform = "Network Devices" + self.db.add_loggedin_relation(cred_id, host_id, shell=self.shell_access) - out = process_secret(self.password) if not self.args.key_file else f"{process_secret(self.password)} (keyfile: {self.args.key_file})" - display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if shell_access else ''}" - self.logger.success(f"{self.username}:{process_secret(out)} {self.mark_pwned()} {highlight(display_shell_access)}") + def check_windows_priv(self, stdout): + if "SeDebugPrivilege" in stdout: + self.admin_privs = True + elif "SeUndockPrivilege" in stdout: + self.admin_privs = True + self.uac = "with UAC - " def check_linux_priv(self): self.admin_privs = False From beee54bea6359fcf445b3a86dc608e47fc28ebff Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 17:50:11 -0500 Subject: [PATCH 07/11] Fix debug logging --- nxc/protocols/ssh.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index f2a7c35c3..9885688ee 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -120,9 +120,9 @@ def plaintext_login(self, username, password, private_key=""): self.check_shell(cred_id) - out = process_secret(self.password) if not self.args.key_file else f"{process_secret(self.password)} (keyfile: {self.args.key_file})" + secret = process_secret(self.password) if not self.args.key_file else f"{process_secret(self.password)} (keyfile: {self.args.key_file})" display_shell_access = f"{self.uac}{self.server_os_platform}{' - Shell access!' if self.shell_access else ''}" - self.logger.success(f"{self.username}:{process_secret(out)} {self.mark_pwned()} {highlight(display_shell_access)}") + self.logger.success(f"{self.username}:{process_secret(secret)} {self.mark_pwned()} {highlight(display_shell_access)}") return True except AuthenticationException as e: if "Private key file is encrypted" in str(e): @@ -164,7 +164,7 @@ def check_shell(self, cred_id): stdout = self.conn.exec_command("whoami /priv")[1].read().decode(self.args.codec, errors="ignore") if stdout: self.server_os_platform = "Windows" - self.logger.debug(f"Windows detected for user: {stdout}") + self.logger.debug("Windows detected") self.shell_access = True self.check_windows_priv(stdout) self.db.add_loggedin_relation(cred_id, host_id, shell=self.shell_access) From 96d23cd174d87b16b769777e5aef0fb0f5d74b39 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 17:52:47 -0500 Subject: [PATCH 08/11] Fix windows output --- nxc/protocols/ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 9885688ee..94ba4af96 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -334,6 +334,6 @@ def execute(self, payload=None, get_output=False): else: self.logger.success("Executed command") if get_output: - for line in stdout.split("\n"): + for line in stdout.replace("\r\n", "\n").rstrip("\n").split("\n"): self.logger.highlight(line.strip("\n")) return stdout From 4303f1d43abc829a973e97f8bdcb79402a85ca1b Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 17:54:53 -0500 Subject: [PATCH 09/11] Moved to init --- nxc/protocols/ssh.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index 94ba4af96..cffb51783 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -184,8 +184,6 @@ def check_windows_priv(self, stdout): self.uac = "with UAC - " def check_linux_priv(self): - self.admin_privs = False - if self.args.sudo_check: self.check_linux_priv_sudo() return From 7a6945a7891fd8fc8cf80aa7b86dde09ca16753e Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 18:20:38 -0500 Subject: [PATCH 10/11] Add missing database handling --- nxc/protocols/ssh.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index cffb51783..f2460b5f0 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -85,7 +85,6 @@ def plaintext_login(self, username, password, private_key=""): try: if self.args.key_file or private_key: self.logger.debug(f"Logging {self.host} with username: {username}, keyfile: {self.args.key_file}") - self.conn.connect( self.host, port=self.port, @@ -97,13 +96,7 @@ def plaintext_login(self, username, password, private_key=""): allow_agent=False, banner_timeout=self.args.ssh_timeout, ) - - cred_id = self.db.add_credential( - "key", - username, - password if password != "" else "", - key=private_key, - ) + cred_id = self.db.add_credential("key", username, password, key=private_key) else: self.logger.debug(f"Logging {self.host} with username: {self.username}, password: {self.password}") self.conn.connect( @@ -151,6 +144,7 @@ def check_shell(self, cred_id): self.server_os_platform = "Linux" self.logger.debug(f"Linux detected for user: {stdout}") self.shell_access = True + self.db.add_loggedin_relation(cred_id, host_id, shell=self.shell_access) self.check_linux_priv() if self.admin_privs: self.logger.debug(f"User {self.username} logged in successfully and is root!") @@ -166,8 +160,14 @@ def check_shell(self, cred_id): self.server_os_platform = "Windows" self.logger.debug("Windows detected") self.shell_access = True - self.check_windows_priv(stdout) self.db.add_loggedin_relation(cred_id, host_id, shell=self.shell_access) + self.check_windows_priv(stdout) + if self.admin_privs: + self.logger.debug(f"User {self.username} logged in successfully and is admin!") + if self.args.key_file: + self.db.add_admin_user("key", self.username, self.password, host_id=host_id, cred_id=cred_id) + else: + self.db.add_admin_user("plaintext", self.username, self.password, host_id=host_id, cred_id=cred_id) return # No shell access From 23b4a23c5dd027894caa433cbaf77a6bcb5332d5 Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Thu, 2 Jan 2025 18:42:11 -0500 Subject: [PATCH 11/11] Readd functionality that key files are stored to db --- nxc/protocols/ssh.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nxc/protocols/ssh.py b/nxc/protocols/ssh.py index f2460b5f0..a5c7b9c9c 100644 --- a/nxc/protocols/ssh.py +++ b/nxc/protocols/ssh.py @@ -90,12 +90,17 @@ def plaintext_login(self, username, password, private_key=""): port=self.port, username=username, passphrase=password if password != "" else None, - key_filename=private_key if private_key else self.args.key_file, + pkey=private_key, + key_filename=self.args.key_file, timeout=self.args.ssh_timeout, look_for_keys=False, allow_agent=False, banner_timeout=self.args.ssh_timeout, ) + # If we get the private key from the file, we need to load it into the database + if self.args.key_file: + with open(self.args.key_file) as f: + private_key = f.read().rstrip("\n") cred_id = self.db.add_credential("key", username, password, key=private_key) else: self.logger.debug(f"Logging {self.host} with username: {self.username}, password: {self.password}")