Skip to content

Commit 5369f88

Browse files
committed
Merge branch 'local_admin_search_enum.rb' of git://github.com/zeknox/metasploit-framework into zeknox-local_admin_search_enum.rb
Conflicts: modules/post/windows/gather/local_admin_search_enum.rb
2 parents f647867 + 5be12c1 commit 5369f88

File tree

1 file changed

+246
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)