1
+ ##
2
+ # ## This file is part of the Metasploit Framework and may be subject to
3
+ # redistribution and commercial restrictions. Please see the Metasploit
4
+ # Framework web site for more information on licensing and terms of use.
5
+ # http://metasploit.com/framework/
6
+ ##
7
+
8
+ require 'msf/core'
9
+ require 'rex'
10
+
11
+ class Metasploit3 < Msf ::Post
12
+
13
+ include Msf ::Post ::Windows ::Priv
14
+ include Msf ::Auxiliary ::Report
15
+ include Msf ::Auxiliary ::Scanner
16
+
17
+ def initialize ( info = { } )
18
+ super (
19
+ 'Name' => 'Windows Local Admin Search' ,
20
+ 'Description' => %q{
21
+ This module will identify systems in a given range that the
22
+ supplied domain user (should migrate into a user pid) has administrative
23
+ access to by using the windows api OpenSCManagerA to establishing a handle
24
+ to the remote host. Additionally it can enumerate logged in users and group
25
+ membership via windows api NetWkstaUserEnum and NetUserGetGroups.
26
+ } ,
27
+ 'License' => MSF_LICENSE ,
28
+ 'Version' => '$Revision: 14767 $' ,
29
+ 'Author' => [ 'Brandon McCann "zeknox" <bmccann [at] accuvant.com>' , 'Thomas McCarthy "smilingraccoon" <smilingraccoon [at] gmail.com>' , 'Royce Davis "r3dy" <rdavis [at] accuvant.com>' ] ,
30
+ 'Platform' => [ 'windows' ] ,
31
+ 'SessionTypes' => [ 'meterpreter' ]
32
+ )
33
+
34
+ register_options (
35
+ [
36
+ OptBool . new ( 'ENUM_USERS' , [ true , 'Enumerates logged on users.' , true ] ) ,
37
+ OptBool . new ( 'ENUM_GROUPS' , [ false , 'Enumerates groups for identified users.' , true ] ) ,
38
+ OptString . new ( 'DOMAIN' , [ false , 'Domain to enumerate user\'s groups for' , nil ] ) ,
39
+ OptString . new ( 'DOMAIN_CONTROLLER' , [ false , 'Domain Controller to query groups' , nil ] )
40
+
41
+ ] , self . class )
42
+ end
43
+
44
+ def run ( )
45
+ if is_system?
46
+ # running as SYSTEM and will not pass any network credentials
47
+ print_error "Running as SYSTEM, module should be run with USER level rights"
48
+ return
49
+ else
50
+ @adv = client . railgun . advapi32
51
+
52
+ # Get domain and domain controller if options left blank
53
+ if datastore [ 'DOMAIN' ] . nil?
54
+ user = client . sys . config . getuid
55
+ datastore [ 'DOMAIN' ] = user . split ( '\\' ) [ 0 ]
56
+ end
57
+
58
+ if datastore [ 'DOMAIN_CONTROLLER' ] . nil? and datastore [ 'ENUM_GROUPS' ]
59
+ @dc_error = false
60
+
61
+ # Uses DC which applied policy since it would be a DC this device normally talks to
62
+ cmd = "gpresult /SCOPE COMPUTER"
63
+ # If Vista/2008 or later add /R
64
+ if ( client . sys . config . sysinfo [ 'OS' ] =~ /Build [6-9]\d \d \d / )
65
+ cmd << " /R"
66
+ end
67
+ res = run_cmd ( cmd )
68
+
69
+ # Check if RSOP data exists, if not disable group check
70
+ unless res =~ /does not have RSOP data./
71
+ datastore [ 'DOMAIN_CONTROLLER' ] = /Group Policy was applied from:\s *(.*)\s */ . match ( res ) [ 1 ] . chomp
72
+ else
73
+ @dc_error = true
74
+ print_error ( "User never logged into device, will not enumerate groups or manually specify DC." )
75
+ end
76
+ end
77
+ super
78
+ end
79
+ end
80
+
81
+ # main contrl method
82
+ def run_host ( ip )
83
+ connect ( ip )
84
+ end
85
+
86
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370669(v=vs.85).aspx
87
+ # enumerate logged in users
88
+ def enum_users ( host )
89
+ begin
90
+ # Connect to host and enumerate logged in users
91
+ winsessions = client . railgun . netapi32 . NetWkstaUserEnum ( "\\ \\ #{ host } " , 1 , 4 , -1 , 4 , 4 , nil )
92
+
93
+ count = winsessions [ 'totalentries' ] * 2
94
+ startmem = winsessions [ 'bufptr' ]
95
+
96
+ base = 0
97
+ userlist = Array . new
98
+ mem = client . railgun . memread ( startmem , 8 *count )
99
+ # For each entry returned, get domain and name of logged in user
100
+ count . times { |i |
101
+ temp = { }
102
+ userptr = mem [ ( base + 0 ) , 4 ] . unpack ( "V*" ) [ 0 ]
103
+ temp [ :user ] = client . railgun . memread ( userptr , 255 ) . split ( "\0 \0 " ) [ 0 ] . split ( "\0 " ) . join
104
+ nameptr = mem [ ( base + 4 ) , 4 ] . unpack ( "V*" ) [ 0 ]
105
+ temp [ :domain ] = client . railgun . memread ( nameptr , 255 ) . split ( "\0 \0 " ) [ 0 ] . split ( "\0 " ) . join
106
+
107
+ # Ignore if empty or machine account
108
+ unless temp [ :user ] . empty? or temp [ :user ] [ -1 , 1 ] == "$"
109
+
110
+ # Check if enumerated user's domain matches supplied domain, if there was
111
+ # an error, or if option disabled
112
+ data = ""
113
+ if datastore [ 'DOMAIN' ] . upcase == temp [ :domain ] . upcase and not @dc_error and datastore [ 'ENUM_GROUPS' ]
114
+ data = " - Groups: #{ enum_groups ( temp [ :user ] ) . chomp ( ", " ) } "
115
+ end
116
+ line = "\t Logged in user:\t #{ temp [ :domain ] } \\ #{ temp [ :user ] } #{ data } \n "
117
+
118
+ # Write user and groups to notes database
119
+ db_note ( host , "#{ temp [ :domain ] } \\ #{ temp [ :user ] } #{ data } " , "localadmin.user.loggedin" )
120
+ userlist << line unless userlist . include? line
121
+
122
+ end
123
+
124
+ base = base + 8
125
+ }
126
+ rescue ::Exception => e
127
+ print_error ( "Issue enumerating users on #{ host } " )
128
+ end
129
+ return userlist
130
+
131
+ end
132
+
133
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370653(v=vs.85).aspx
134
+ # Enumerate groups for identified users
135
+ def enum_groups ( user )
136
+ grouplist = ""
137
+
138
+ dc = "\\ \\ #{ datastore [ 'DOMAIN_CONTROLLER' ] } "
139
+ begin
140
+ # Connect to DC and enumerate groups of user
141
+ usergroups = client . railgun . netapi32 . NetUserGetGroups ( dc , user , 0 , 4 , -1 , 4 , 4 )
142
+
143
+ rescue ::Exception => e
144
+ print_error ( "Issue connecting to DC, try manually setting domain and DC" )
145
+ end
146
+
147
+ count = usergroups [ 'totalentries' ]
148
+ startmem = usergroups [ 'bufptr' ]
149
+ base = 0
150
+
151
+ begin
152
+ mem = client . railgun . memread ( startmem , 8 *count )
153
+ rescue ::Exception => e
154
+ print_error ( "Issue reading memory for groups for user #{ user } " )
155
+ end
156
+
157
+ begin
158
+ # For each entry returned, get group
159
+ count . to_i . times { |i |
160
+ temp = { }
161
+ groupptr = mem [ ( base + 0 ) , 4 ] . unpack ( "V*" ) [ 0 ]
162
+ temp [ :group ] = client . railgun . memread ( groupptr , 255 ) . split ( "\0 \0 " ) [ 0 ] . split ( "\0 " ) . join
163
+
164
+ # Add group to string to be returned
165
+ grouplist << "#{ temp [ :group ] } , "
166
+ if ( i % 5 ) == 2
167
+ grouplist <<"\n\t- "
168
+ end
169
+ base = base + 4
170
+ }
171
+
172
+ rescue ::Exception => e
173
+ print_error("Issue enumerating groups for user #{ user } , check domain")
174
+ end
175
+
176
+ return grouplist.chomp("\n\t- ")
177
+
178
+ end
179
+
180
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx
181
+ # method to connect to remote host using windows api
182
+ def connect(host)
183
+ user = client.sys.config.getuid
184
+ # use railgun and OpenSCManagerA api to connect to remote host
185
+ manag = @adv.OpenSCManagerA("\\ \\ #{ host } ", nil, 0xF003F) # SC_MANAGER_ALL_ACCESS
186
+
187
+ if(manag["return"] != 0) # we have admin rights
188
+ result = "#{ host . ljust ( 16 ) } #{ user } - Local admin found\n"
189
+ # Run enumerate users on all hosts if option was set
190
+
191
+ if datastore['ENUM_USERS']
192
+ enum_users(host).each {|i|
193
+ result << i
194
+ }
195
+ end
196
+
197
+ # close the handle if connection was made
198
+ @adv.CloseServiceHandle(manag["return"])
199
+ # Append data to loot table within database
200
+ db_loot(host, user, "localadmin.user")
201
+ print_good(result.chomp("\n")) unless result.nil?
202
+ else
203
+ # we dont have admin rights
204
+ print_error("#{ host . ljust ( 16 ) } #{ user } - No Local Admin rights")
205
+ end
206
+ end
207
+
208
+ # From enum_domain_group_users.rb by Carlos Perez and Stephen Haywood
209
+ # Run command, return results
210
+ def run_cmd(cmd)
211
+ process = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true})
212
+ res = ""
213
+
214
+ while (d = process.channel.read)
215
+ break if d == ""
216
+ res << d
217
+ end
218
+
219
+ process.channel.close
220
+ process.close
221
+ return res
222
+ end
223
+
224
+ # Write to notes database
225
+ def db_note(host, data, type)
226
+ if db
227
+ report_note(
228
+ :type => type,
229
+ :data => data,
230
+ :host => host,
231
+ :update => :unique_data
232
+ )
233
+ end
234
+ end
235
+
236
+ # Write to loot database
237
+ def db_loot(host, user, type)
238
+ if db
239
+ store_loot(
240
+ type,
241
+ 'text/plain',
242
+ host,
243
+ "#{ host } :#{ user } ",
244
+ 'hosts_localadmin.txt',
245
+ user
246
+ )
247
+ end
248
+ end
249
+ end
0 commit comments