Skip to content

Commit cf99d9c

Browse files
committed
better mssql enum login
1 parent 31acd22 commit cf99d9c

1 file changed

Lines changed: 48 additions & 11 deletions

File tree

nxc/modules/enum_logins.py

Lines changed: 48 additions & 11 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,62 @@ 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

2831
def get_logins(self) -> list:
2932
"""
30-
Fetches a list of SQL Server logins.
33+
Fetches a list of SQL Server logins with their types.
3134
3235
Returns
3336
-------
34-
list: List of login names.
37+
list: List of tuples containing (login_name, login_type, status).
38+
"""
39+
query = """
40+
SELECT
41+
name,
42+
type,
43+
type_desc,
44+
CASE type_desc
45+
WHEN 'SQL_LOGIN' THEN 'SQL User'
46+
WHEN 'WINDOWS_LOGIN' THEN
47+
CASE
48+
WHEN name LIKE '%\\%' THEN 'Domain User'
49+
ELSE 'Local User'
50+
END
51+
WHEN 'WINDOWS_GROUP' THEN 'Windows Group'
52+
WHEN 'CERTIFICATE_MAPPED_LOGIN' THEN 'Certificate Login'
53+
WHEN 'ASYMMETRIC_KEY_MAPPED_LOGIN' THEN 'Asymmetric Key Login'
54+
ELSE type_desc
55+
END as login_type,
56+
is_disabled,
57+
create_date
58+
FROM sys.server_principals
59+
WHERE type IN ('S', 'U', 'G', 'C', 'K')
60+
AND name NOT LIKE '##%'
61+
AND name NOT IN ('sa') OR name = 'sa'
62+
ORDER BY login_type, name;
3563
"""
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 []
64+
try:
65+
res = self.mssql_conn.sql_query(query)
66+
if res:
67+
logins = []
68+
for login in res:
69+
status = "DISABLED" if login.get("is_disabled") else "ENABLED"
70+
logins.append((login["name"], login["login_type"], status))
71+
return logins
72+
return []
73+
except Exception as e:
74+
self.context.log.fail(f"Error querying logins: {e}")
75+
return []
3976

4077
def options(self, context, module_options):
4178
pass

0 commit comments

Comments
 (0)