11class 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