From 363691aa4fcbdfbd2c2e266b1ce5a1032d3de31b Mon Sep 17 00:00:00 2001 From: 0xQRx Date: Sat, 24 Aug 2024 18:09:12 -0400 Subject: [PATCH 1/3] added is_xp_cmdshell_enabled() function to check mssql if xp_cmdshell is already enabled, to avoid altering its state --- nxc/protocols/mssql/mssqlexec.py | 33 ++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/nxc/protocols/mssql/mssqlexec.py b/nxc/protocols/mssql/mssqlexec.py index 3fe0bb8e..ca90b8c8 100755 --- a/nxc/protocols/mssql/mssqlexec.py +++ b/nxc/protocols/mssql/mssqlexec.py @@ -8,11 +8,19 @@ class MSSQLEXEC: def execute(self, command): result = None + xp_cmdshell_was_enabled = False + try: - self.logger.debug("Attempting to enable xp cmd shell") - self.enable_xp_cmdshell() + xp_cmdshell_was_enabled = self.is_xp_cmdshell_enabled() + if not xp_cmdshell_was_enabled: + self.logger.debug("xp_cmdshell is disabled, attempting to enable it.") + self.enable_xp_cmdshell() + else: + self.logger.debug("xp_cmdshell is already enabled.") + except Exception as e: - self.logger.error(f"Error when attempting to enable x_cmdshell: {e}") + self.logger.error(f"Error when checking/enabling xp_cmdshell: {e}") + try: cmd = f"exec master..xp_cmdshell '{command}'" self.logger.debug(f"Attempting to execute query: {cmd}") @@ -21,19 +29,32 @@ class MSSQLEXEC: if result: result = "\n".join(line["output"] for line in result if line["output"] != "NULL") self.logger.debug(f"Concatenated result together for easier parsing: {result}") - # if you prepend SilentlyContinue it will still output the error, but it will still continue on (so it's not silent...) if "Preparing modules for first use" in result and "Completed" not in result: self.logger.error("Error when executing PowerShell (received 'preparing modules for first use'), try prepending $ProgressPreference = 'SilentlyContinue'; to your command") except Exception as e: self.logger.error(f"Error when attempting to execute command via xp_cmdshell: {e}") try: - self.logger.debug("Attempting to disable xp cmd shell") - self.disable_xp_cmdshell() + if not xp_cmdshell_was_enabled: + self.logger.debug("xp_cmdshell was not enabled originally, attempting to disable it.") + self.disable_xp_cmdshell() + else: + self.logger.debug("xp_cmdshell was originally enabled, leaving it enabled.") except Exception as e: self.logger.error(f"[OPSEC] Error when attempting to disable xp_cmdshell: {e}") + return result + def is_xp_cmdshell_enabled(self): + query = "EXEC sp_configure 'xp_cmdshell';" + self.logger.debug(f"Checking if xp_cmdshell is enabled: {query}") + result = self.mssql_conn.sql_query(query) + # Assuming the query returns a list of dictionaries with 'config_value' as the key + self.logger.debug(f"xp_cmdshell check result: {result}") + if result and result[0]["config_value"] == 1: + return True + return False + def enable_xp_cmdshell(self): query = "exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'xp_cmdshell', 1;RECONFIGURE;" self.logger.debug(f"Executing query: {query}") From a8954f1f32ca86782d8d0109feb2b180f3c5cd0f Mon Sep 17 00:00:00 2001 From: 0xQRx <157332395+0xQRx@users.noreply.github.com> Date: Sat, 24 Aug 2024 18:52:46 -0400 Subject: [PATCH 2/3] Restore removed comment. Signed-off-by: 0xQRx <157332395+0xQRx@users.noreply.github.com> --- nxc/protocols/mssql/mssqlexec.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxc/protocols/mssql/mssqlexec.py b/nxc/protocols/mssql/mssqlexec.py index ca90b8c8..df4ff0b5 100755 --- a/nxc/protocols/mssql/mssqlexec.py +++ b/nxc/protocols/mssql/mssqlexec.py @@ -29,6 +29,7 @@ class MSSQLEXEC: if result: result = "\n".join(line["output"] for line in result if line["output"] != "NULL") self.logger.debug(f"Concatenated result together for easier parsing: {result}") + # if you prepend SilentlyContinue it will still output the error, but it will still continue on (so it's not silent...) if "Preparing modules for first use" in result and "Completed" not in result: self.logger.error("Error when executing PowerShell (received 'preparing modules for first use'), try prepending $ProgressPreference = 'SilentlyContinue'; to your command") except Exception as e: From c012e04ecf413cb4b928eb3dee5f804268a65f5a Mon Sep 17 00:00:00 2001 From: Alexander Neff Date: Wed, 6 Nov 2024 16:34:41 -0500 Subject: [PATCH 3/3] Add backup&restore options for mssql options, to keep the current state of the mssql config --- nxc/protocols/mssql/mssqlexec.py | 87 +++++++++++++++----------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/nxc/protocols/mssql/mssqlexec.py b/nxc/protocols/mssql/mssqlexec.py index df4ff0b5..46fd7b8e 100755 --- a/nxc/protocols/mssql/mssqlexec.py +++ b/nxc/protocols/mssql/mssqlexec.py @@ -6,20 +6,14 @@ class MSSQLEXEC: self.mssql_conn = connection self.logger = logger + # Store the original state of options that have to be enabled/disabled in order to restore them later + self.backuped_options = {} + def execute(self, command): result = None - xp_cmdshell_was_enabled = False - try: - xp_cmdshell_was_enabled = self.is_xp_cmdshell_enabled() - if not xp_cmdshell_was_enabled: - self.logger.debug("xp_cmdshell is disabled, attempting to enable it.") - self.enable_xp_cmdshell() - else: - self.logger.debug("xp_cmdshell is already enabled.") - - except Exception as e: - self.logger.error(f"Error when checking/enabling xp_cmdshell: {e}") + self.backup_and_enable("advanced options") + self.backup_and_enable("xp_cmdshell") try: cmd = f"exec master..xp_cmdshell '{command}'" @@ -35,56 +29,57 @@ class MSSQLEXEC: except Exception as e: self.logger.error(f"Error when attempting to execute command via xp_cmdshell: {e}") - try: - if not xp_cmdshell_was_enabled: - self.logger.debug("xp_cmdshell was not enabled originally, attempting to disable it.") - self.disable_xp_cmdshell() - else: - self.logger.debug("xp_cmdshell was originally enabled, leaving it enabled.") - except Exception as e: - self.logger.error(f"[OPSEC] Error when attempting to disable xp_cmdshell: {e}") - + self.restore("xp_cmdshell") + self.restore("advanced options") + return result - def is_xp_cmdshell_enabled(self): - query = "EXEC sp_configure 'xp_cmdshell';" - self.logger.debug(f"Checking if xp_cmdshell is enabled: {query}") + def restore(self, option): + try: + if not self.backuped_options[option]: + self.logger.debug(f"Option '{option}' was not enabled originally, attempting to disable it.") + query = f"EXEC master.dbo.sp_configure '{option}', 0;RECONFIGURE;" + self.logger.debug(f"Executing query: {query}") + self.mssql_conn.sql_query(query) + else: + self.logger.debug(f"Option '{option}' was originally enabled, leaving it enabled.") + except Exception as e: + self.logger.error(f"[OPSEC] Error when attempting to restore option '{option}': {e}") + + def backup_and_enable(self, option): + try: + self.backuped_options[option] = self.is_option_enabled("show advanced options") + if not self.backuped_options[option]: + self.logger.debug(f"Option '{option}' is disabled, attempting to enable it.") + query = f"EXEC master.dbo.sp_configure '{option}', 1;RECONFIGURE;" + self.logger.debug(f"Executing query: {query}") + self.mssql_conn.sql_query(query) + else: + self.logger.debug(f"Option '{option}' is already enabled.") + except Exception as e: + self.logger.error(f"Error when checking/enabling option '{option}': {e}") + + def is_option_enabled(self, option): + query = f"EXEC master.dbo.sp_configure '{option}';" + self.logger.debug(f"Checking if {option} is enabled: {query}") result = self.mssql_conn.sql_query(query) # Assuming the query returns a list of dictionaries with 'config_value' as the key - self.logger.debug(f"xp_cmdshell check result: {result}") + self.logger.debug(f"{option} check result: {result}") if result and result[0]["config_value"] == 1: return True return False - def enable_xp_cmdshell(self): - query = "exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'xp_cmdshell', 1;RECONFIGURE;" - self.logger.debug(f"Executing query: {query}") - self.mssql_conn.sql_query(query) - - def disable_xp_cmdshell(self): - query = "exec sp_configure 'xp_cmdshell', 0 ;RECONFIGURE;exec sp_configure 'show advanced options', 0 ;RECONFIGURE;" - self.logger.debug(f"Executing query: {query}") - self.mssql_conn.sql_query(query) - - def enable_ole(self): - query = "exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'Ole Automation Procedures', 1;RECONFIGURE;" - self.logger.debug(f"Executing query: {query}") - self.mssql_conn.sql_query(query) - - def disable_ole(self): - query = "exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'Ole Automation Procedures', 0;RECONFIGURE;" - self.logger.debug(f"Executing query: {query}") - self.mssql_conn.sql_query(query) - def put_file(self, data, remote): try: - self.enable_ole() + self.backup_and_enable("advanced options") + self.backup_and_enable("Ole Automation Procedures") hexdata = data.hex() self.logger.debug(f"Hex data to write to file: {hexdata}") query = f"DECLARE @ob INT;EXEC sp_OACreate 'ADODB.Stream', @ob OUTPUT;EXEC sp_OASetProperty @ob, 'Type', 1;EXEC sp_OAMethod @ob, 'Open';EXEC sp_OAMethod @ob, 'Write', NULL, 0x{hexdata};EXEC sp_OAMethod @ob, 'SaveToFile', NULL, '{remote}', 2;EXEC sp_OAMethod @ob, 'Close';EXEC sp_OADestroy @ob;" self.logger.debug(f"Executing query: {query}") self.mssql_conn.sql_query(query) - self.disable_ole() + self.restore("Ole Automation Procedures") + self.restore("advanced options") except Exception as e: self.logger.debug(f"Error uploading via mssqlexec: {e}")