, Thomas McCarthy "smilingraccoon" Data Build on a solid foundation with Vulners data We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data Api Power your application with Vulners API The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access App Assess and manage vulnerabilities with Vulners tools Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediationWindows Gather Local Admin Search
![]()
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Local Admin Search',
'Description' => %q{
This module will identify systems in a given range that the
supplied domain user (should migrate into a user pid) has administrative
access to by using the Windows API OpenSCManagerA to establishing a handle
to the remote host. Additionally it can enumerate logged in users and group
membership via Windows API NetWkstaUserEnum and NetUserGetGroups.
},
'License' => MSF_LICENSE,
'Author' => [
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>',
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
'Royce Davis "r3dy" <rdavis[at]accuvant.com>'
],
'Platform' => 'win',
'SessionTypes' => [ 'meterpreter' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_railgun_api
stdapi_railgun_memread
stdapi_sys_config_getuid
]
}
}
)
)
register_options(
[
OptBool.new('ENUM_USERS', [ true, 'Enumerates logged on users.', true]),
OptBool.new('ENUM_GROUPS', [ false, 'Enumerates groups for identified users.', true]),
OptString.new('DOMAIN', [false, 'Domain to enumerate user\'s groups for']),
OptString.new('DOMAIN_CONTROLLER', [false, 'Domain Controller to query groups'])
]
)
end
def setup
super
# This datastore option can be modified during runtime.
# Saving it here so the modified value remains with this module.
@domain_controller = datastore['DOMAIN_CONTROLLER']
if is_system?
# running as SYSTEM and will not pass any network credentials
print_error 'Running as SYSTEM, module should be run with USER level rights'
return
else
@adv = client.railgun.advapi32
# Get domain and domain controller if options left blank
if datastore['DOMAIN'].nil? || datastore['DOMAIN'].empty?
user = client.sys.config.getuid
datastore['DOMAIN'] = user.split('\\')[0]
end
if @domain_controller.nil? && datastore['ENUM_GROUPS']
@dc_error = false
# Uses DC which applied policy since it would be a DC this device normally talks to
cmd = 'gpresult /SCOPE COMPUTER'
# If Vista/2008 or later add /R
version = get_version_info
if version.build_number >= Msf::WindowsVersion::Vista_SP0
cmd << ' /R'
end
res = cmd_exec('cmd.exe', "/c #{cmd}")
# Check if RSOP data exists, if not disable group check
if res =~ /does not have RSOP data./
@dc_error = true
print_error('User never logged into device, will not enumerate users and groups. Manually specify DC.')
else
dc_applied = /Group Policy was applied from:\s*(.*)\s*/.match(res)
if dc_applied
@domain_controller = dc_applied[1].strip
else
@dc_error = true
print_error('Could not read RSOP data, will not enumerate users and groups. Manually specify DC.')
end
end
end
end
end
# main control method
def run_host(ip)
connect(ip)
end
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa370669(v=vs.85).aspx
# enumerate logged in users
def enum_users(host)
userlist = Array.new
begin
# Connect to host and enumerate logged in users
winsessions = client.railgun.netapi32.NetWkstaUserEnum("\\\\#{host}", 1, 4, -1, 4, 4, nil)
rescue ::Exception
print_error("Issue enumerating users on #{host}")
return userlist
end
return userlist if winsessions.nil?
count = winsessions['totalentries'] * 2
startmem = winsessions['bufptr']
base = 0
userlist = Array.new
begin
mem = client.railgun.memread(startmem, 8 * count)
rescue ::Exception => e
print_error("Issue reading memory for #{host}")
vprint_error(e.to_s)
return userlist
end
# For each entry returned, get domain and name of logged in user
begin
count.times do |_i|
temp = {}
userptr = mem[(base + 0), 4].unpack('V*')[0]
temp[:user] = client.railgun.memread(userptr, 255).split("\0\0")[0].split("\0").join
nameptr = mem[(base + 4), 4].unpack('V*')[0]
temp[:domain] = client.railgun.memread(nameptr, 255).split("\0\0")[0].split("\0").join
# Ignore if empty or machine account
unless temp[:user].empty? || (temp[:user][-1, 1] == '$')
# Check if enumerated user's domain matches supplied domain, if there was
# an error, or if option disabled
data = ''
if (datastore['DOMAIN'].upcase == temp[:domain].upcase) && !@dc_error && datastore['ENUM_GROUPS']
data << " - Groups: #{enum_groups(temp[:user]).chomp(', ')}"
end
line = "\tLogged in user:\t#{temp[:domain]}\\#{temp[:user]}#{data}\n"
# Write user and groups to notes database
db_note(host, "#{temp[:domain]}\\#{temp[:user]}#{data}", 'localadmin.user.loggedin')
userlist << line unless userlist.include? line
end
base += 8
end
rescue ::Exception => e
print_error("Issue enumerating users on #{host}")
vprint_error(e.backtrace)
end
return userlist
end
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa370653(v=vs.85).aspx
# Enumerate groups for identified users
def enum_groups(user)
grouplist = ''
dc = "\\\\#{@domain_controller}"
begin
# Connect to DC and enumerate groups of user
usergroups = client.railgun.netapi32.NetUserGetGroups(dc, user, 0, 4, -1, 4, 4)
rescue ::Exception => e
print_error('Issue connecting to DC, try manually setting domain and DC')
vprint_error(e.to_s)
return grouplist
end
count = usergroups['totalentries']
startmem = usergroups['bufptr']
base = 0
begin
mem = client.railgun.memread(startmem, 8 * count)
rescue ::Exception => e
print_error("Issue reading memory for groups for user #{user}")
vprint_error(e.to_s)
return grouplist
end
begin
# For each entry returned, get group
count.to_i.times do |i|
temp = {}
groupptr = mem[(base + 0), 4].unpack('V*')[0]
temp[:group] = client.railgun.memread(groupptr, 255).split("\0\0")[0].split("\0").join
# Add group to string to be returned
grouplist << "#{temp[:group]}, "
if (i % 5) == 2
grouplist << "\n\t- "
end
base += 4
end
rescue ::Exception => e
print_error("Issue enumerating groups for user #{user}, check domain")
vprint_error(e.backtrace)
return grouplist
end
return grouplist.chomp("\n\t- ")
end
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx
# method to connect to remote host using windows api
def connect(host)
if @adv.nil?
return
end
user = client.sys.config.getuid
# use railgun and OpenSCManagerA api to connect to remote host
manag = @adv.OpenSCManagerA("\\\\#{host}", nil, 0xF003F) # SC_MANAGER_ALL_ACCESS
if (manag['return'] != 0) # we have admin rights
result = "#{host.ljust(16)} #{user} - Local admin found\n"
# Run enumerate users on all hosts if option was set
if datastore['ENUM_USERS']
enum_users(host).each do |i|
result << i
end
end
# close the handle if connection was made
@adv.CloseServiceHandle(manag['return'])
# Append data to loot table within database
print_good(result.chomp("\n")) unless result.nil?
db_loot(host, user, 'localadmin.user')
else
# we dont have admin rights
print_error("#{host.ljust(16)} #{user} - No Local Admin rights")
end
end
# Write to notes database
def db_note(host, data, type)
report_note(
type: type,
data: data,
host: host,
update: :unique_data
)
end
# Write to loot database
def db_loot(host, user, type)
p = store_loot(type, 'text/plain', host, "#{host}:#{user}", 'hosts_localadmin.txt', user)
vprint_good("User data stored in: #{p}")
end
end