Skip to content

Commit 26c3f66

Browse files
authored
Merge pull request Pennyw0rth#755 from Pennyw0rth/mssql
Update mssql enum login
2 parents e446d36 + 1ffe908 commit 26c3f66

2 files changed

Lines changed: 76 additions & 16 deletions

File tree

nxc/modules/enum_logins.py

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
class NXCModule:
22
"""
3-
Enumerate SQL Server logins
4-
Module by deathflamingo
3+
Enumerate SQL Server logins (SQL, Domain, Local users)
4+
Module by deathflamingo, modified by mpgn
55
"""
66

77
name = "enum_logins"
8-
description = "Enumerate SQL Server logins"
8+
description = "Enumerate SQL Server logins (SQL, Domain, Local users)"
99
supported_protocols = ["mssql"]
1010
opsec_safe = True
1111
multiple_hosts = True
@@ -17,25 +17,69 @@ def __init__(self):
1717
def on_login(self, context, connection):
1818
self.context = context
1919
self.mssql_conn = connection.conn
20+
2021
logins = self.get_logins()
2122
if logins:
22-
self.context.log.success("Logins found:")
23-
for login in logins:
24-
self.context.log.display(f" - {login}")
23+
self.context.log.display("Enumerated logins")
24+
self.context.log.highlight(f"{'Login Name':<35} {'Type':<15} {'Status'}")
25+
self.context.log.highlight(f"{'----------':<35} {'----':<15} {'------'}")
26+
for login_name, login_type, status in logins:
27+
self.context.log.highlight(f"{login_name:<35} {login_type:<15} {status}")
2528
else:
2629
self.context.log.fail("No logins found.")
2730

31+
def get_domain_name(self) -> str:
32+
query = "SELECT DEFAULT_DOMAIN() as domain_name;"
33+
try:
34+
res = self.mssql_conn.sql_query(query)
35+
if res and res[0].get("domain_name"):
36+
return res[0]["domain_name"].upper()
37+
return ""
38+
except Exception as e:
39+
self.context.log.debug(f"Error querying domain name: {e}")
40+
return ""
41+
2842
def get_logins(self) -> list:
29-
"""
30-
Fetches a list of SQL Server logins.
43+
domain_name = self.get_domain_name()
44+
domain_prefix = f"{domain_name}\\" if domain_name else ""
3145

32-
Returns
33-
-------
34-
list: List of login names.
46+
query = f"""
47+
SELECT
48+
name,
49+
type,
50+
type_desc,
51+
CASE type_desc
52+
WHEN 'SQL_LOGIN' THEN 'SQL User'
53+
WHEN 'WINDOWS_LOGIN' THEN
54+
CASE
55+
WHEN name LIKE '{domain_prefix}%' THEN 'Domain User'
56+
WHEN name LIKE '%\\%' THEN 'Local User'
57+
ELSE 'Local User'
58+
END
59+
WHEN 'WINDOWS_GROUP' THEN 'Windows Group'
60+
WHEN 'CERTIFICATE_MAPPED_LOGIN' THEN 'Certificate Login'
61+
WHEN 'ASYMMETRIC_KEY_MAPPED_LOGIN' THEN 'Asymmetric Key Login'
62+
ELSE type_desc
63+
END as login_type,
64+
is_disabled,
65+
create_date
66+
FROM sys.server_principals
67+
WHERE type IN ('S', 'U', 'G', 'C', 'K')
68+
AND name NOT LIKE '##%'
69+
ORDER BY login_type, name;
3570
"""
36-
query = "SELECT name FROM sys.server_principals WHERE type_desc = 'SQL_LOGIN';"
37-
res = self.mssql_conn.sql_query(query)
38-
return [login["name"] for login in res] if res else []
71+
try:
72+
res = self.mssql_conn.sql_query(query)
73+
if res:
74+
logins = []
75+
for login in res:
76+
status = "DISABLED" if login.get("is_disabled") else "ENABLED"
77+
logins.append((login["name"], login["login_type"], status))
78+
return logins
79+
return []
80+
except Exception as e:
81+
self.context.log.fail(f"Error querying logins: {e}")
82+
return []
3983

4084
def options(self, context, module_options):
4185
pass

nxc/modules/link_xpcmd.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,21 @@ def on_login(self, context, connection):
3131
return
3232

3333
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}")
34+
query = f"EXEC ('xp_cmdshell ''{self.command}''') AT [{self.linked_server}]"
35+
result = self.mssql_conn.sql_query(query)
36+
37+
if result:
38+
output_lines = []
39+
for row in result:
40+
output_value = row.get("output")
41+
if output_value and output_value != "NULL":
42+
output_lines.append(str(output_value))
43+
44+
if output_lines:
45+
self.context.log.success("Executed command via linked server")
46+
for line in output_lines:
47+
self.context.log.highlight(line.strip())
48+
else:
49+
self.context.log.display("Command executed but returned no output")
50+
else:
51+
self.context.log.fail("No result returned from query")

0 commit comments

Comments
 (0)