Land #2107 Post Enum Domain Users

bug/bundler_fix
Meatballs 2014-04-09 11:32:12 +01:00
commit ae3ead6ef9
No known key found for this signature in database
GPG Key ID: 5380EAF01F2F8B38
4 changed files with 272 additions and 51 deletions

View File

@ -0,0 +1,152 @@
# -*- coding: binary -*-
module Msf
class Post
module Windows
module NetAPI
MAX_PREFERRED_LENGTH = -1
SV_TYPE_ALL = 0xFFFFFFFF
SV_TYPE_DOMAIN_ENUM = 0x80000000
SV_TYPE_DOMAIN_BAKCTRL = 10
SV_TYPE_DOMAIN_CTRL = 4
ERROR_ACCESS_DENIED = 5
ERROR_NOT_ENOUGH_MEMORY = 8
ERROR_INVALID_PARAMETER = 87
ERROR_INVALID_LEVEL = 124
ERROR_MORE_DATA = 234
ERROR_NO_BROWSER_SERVERS_FOUND = 6118
NERR_ClientNameNotFound = 2312
NERR_InvalidComputer = 2351
NERR_UserNotFound = 2221
def UnicodeByteStringToAscii(str)
length = (str.index "\0\0\0") + 1
Rex::Text.to_ascii(str[0..length])
end
def netapi_buffer_free(ptr)
# Free the buffer
ret = client.railgun.netapi32.NetApiBufferFree(ptr)
vprint_error("Unable to free buffer, Error Code: #{ret['return']}") unless ret['return'] == 0
end
def net_server_enum(server_type=SV_TYPE_ALL, domain=nil)
hosts = []
result = client.railgun.netapi32.NetServerEnum(
nil, # servername
100, # level (100/101)
4, # bufptr
MAX_PREFERRED_LENGTH, # prefmaxlen
4, # entries read
4, # total entries
server_type, # server_type
domain, # domain
nil # resume handle
)
case result['return']
when 0
hosts = read_server_structs(result['bufptr'], result['totalentries'], domain, server_type)
when ERROR_NO_BROWSER_SERVERS_FOUND
print_error("ERROR_NO_BROWSER_SERVERS_FOUND")
return nil
when ERROR_MORE_DATA
vprint_error("ERROR_MORE_DATA")
return nil
end
netapi_buffer_free(result['bufptr'])
return hosts
end
def read_server_structs(start_ptr, count, domain, server_type)
base = 0
struct_size = 8
hosts = []
mem = client.railgun.memread(start_ptr, struct_size*count)
count.times do
x = {}
x[:version]= mem[(base + 0),4].unpack("V*")[0]
nameptr = mem[(base + 4),4].unpack("V*")[0]
x[:name] = UnicodeByteStringToAscii(client.railgun.memread(nameptr, 255))
hosts << x
base += struct_size
end
return hosts
end
def net_session_enum(hostname, username)
sessions = []
result = client.railgun.netapi32.NetSessionEnum(
hostname, # servername
nil, # UncClientName
username, # username
10, # level
4, # bufptr
MAX_PREFERRED_LENGTH, # prefmaxlen
4, # entriesread
4, # totalentries
nil # resume_handle
)
case result['return']
when 0
vprint_error("#{hostname} Session identified")
sessions = read_session_structs(result['bufptr'], result['totalentries'], hostname)
when ERROR_ACCESS_DENIED
vprint_error("#{hostname} Access denied...")
return nil
when 53
vprint_error("Host not found or did not respond: #{hostname}")
return nil
when 123
vprint_error("Invalid host: #{hostname}")
return nil
when NERR_UserNotFound
return nil
when ERROR_MORE_DATA
vprint_error("#{hostname} ERROR_MORE_DATA")
else
vprint_error("Unaccounted for error code: #{result['return']}")
return nil
end
netapi_buffer_free(result['bufptr'])
return sessions
end
def read_session_structs(start_ptr, count, hostname)
base = 0
struct_size = 16
sessions = []
mem = client.railgun.memread(start_ptr, struct_size*count)
count.times do
sess = {}
cnameptr = mem[(base + 0),4].unpack("V*")[0]
usernameptr = mem[(base + 4),4].unpack("V*")[0]
sess[:usetime] = mem[(base + 8),4].unpack("V*")[0]
sess[:idletime] = mem[(base + 12),4].unpack("V*")[0]
sess[:cname] = UnicodeByteStringToAscii(client.railgun.memread(cnameptr,255))
sess[:username] = UnicodeByteStringToAscii(client.railgun.memread(usernameptr,255))
sess[:hostname] = hostname
sessions << sess
base = base + struct_size
end
return sessions
end
end # NetAPI
end # Windows
end # Post
end # Msf

View File

@ -68,6 +68,22 @@ class Def_netapi32
["PDWORD","totalentries","out"]
])
dll.add_function('NetSessionEnum', 'DWORD',[
['PWCHAR','servername','in'],
['PWCHAR','UncClientName','in'],
['PWCHAR','username','in'],
['DWORD','level','in'],
['PDWORD','bufptr','out'],
['DWORD','prefmaxlen','in'],
['PDWORD','entriesread','out'],
['PDWORD','totalentries','out'],
['PDWORD','resume_handle','inout']
])
dll.add_function('NetApiBufferFree', 'DWORD', [
['LPVOID','buffer','in']
])
return dll
end

View File

@ -0,0 +1,92 @@
require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/windows/registry'
require 'msf/core/post/windows/netapi'
class Metasploit3 < Msf::Post
include Msf::Post::Common
include Msf::Post::Windows::Registry
include Msf::Post::Windows::NetAPI
include Msf::Post::Windows::Accounts
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Gather Enumerate Active Domain Users',
'Description' => %q{
This module will enumerate computers included in the primary Domain and attempt
to list all locations the targeted user has sessions on. If a the HOST option is specified
the module will target only that host. If the HOST is specified and USER is set to nil, all users
logged into that host will be returned.'
},
'License' => MSF_LICENSE,
'Author' => [
'Etienne Stalmans <etienne[at]sensepost.com>',
'Ben Campbell <eat_meatballs[at]hotmail.co.uk>'
],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ]
))
register_options(
[
OptString.new('USER', [false, 'Target User for NetSessionEnum']),
OptString.new('HOST', [false, 'Target a specific host']),
], self.class)
end
def run
sessions = []
user = datastore['USER']
host = datastore['HOST']
if host
if user
print_status("Attempting to identify #{user} on #{host}...")
else
print_status("Attempting to get all logged in users on #{host}...")
end
sessions = net_session_enum(host, user)
elsif user
# Domain must be NETBIOS style rather than DNS style
domain = get_domain
if domain.blank?
fail_with(Failure::Unknown, "Machine is not part of a domain.")
else
domain = domain.split('.').first.upcase
print_status("Using domain: #{domain}")
print_status("Getting list of domain hosts...")
end
hosts = net_server_enum(SV_TYPE_ALL, domain)
if hosts
len = hosts.count
print_status("#{len} host(s) found")
hosts.each do |host|
sessions << net_session_enum(host[:name], user)
end
end
sessions.flatten!
else
fail_with(Failure::BadConfig, "Invalid options, either HOST or USER must be specified.")
end
if sessions.nil? or sessions.count == 0
fail_with(Failure::Unknown, "No sessions found")
else
print_status("#{sessions.count} session(s) identified")
sessions.each do |s|
if s
print_good("#{s[:username]} logged in at #{s[:hostname]} and has been idle for #{s[:idletime]} seconds")
end
end
end
end
end

View File

@ -5,9 +5,12 @@
require 'msf/core'
require 'rex'
require 'msf/core/post/windows/netapi'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::NetAPI
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Gather Domain Enumeration',
@ -23,69 +26,27 @@ class Metasploit3 < Msf::Post
end
def run
domains = net_server_enum(SV_TYPE_DOMAIN_ENUM)
domain_enum = 2147483648 # SV_TYPE_DOMAIN_ENUM = hex 80000000
buffersize = 500
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domain_enum,nil,nil)
print_status("Finding the right buffersize...")
while result['return'] == 234
print_status("Tested #{buffersize}, got #{result['entriesread']} of #{result['totalentries']}")
buffersize = buffersize + 500
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domain_enum,nil,nil)
end
domains.each do |domain|
print_status("Enumerating DCs for #{domain[:name]}")
dcs = net_server_enum(SV_TYPE_DOMAIN_BAKCTRL | SV_TYPE_DOMAIN_CTRL, domain[:name])
count = result['totalentries']
print_status("#{count} domain(s) found.")
startmem = result['bufptr']
base = 0
domains = []
mem = client.railgun.memread(startmem, 8*count)
count.times{|i|
x = {}
x[:platform] = mem[(base + 0),4].unpack("V*")[0]
nameptr = mem[(base + 4),4].unpack("V*")[0]
x[:domain] = client.railgun.memread(nameptr,255).split("\0\0")[0].split("\0").join
domains << x
base = base + 8
}
domaincontrollers = 24 # 10 + 8 (SV_TYPE_DOMAIN_BAKCTRL || SV_TYPE_DOMAIN_CTRL)
domains.each do |x|
print_status("Enumerating DCs for #{x[:domain]}")
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domaincontrollers,x[:domain],nil)
while result['return'] == 234
buffersize = buffersize + 500
result = client.railgun.netapi32.NetServerEnum(nil,100,4,buffersize,4,4,domaincontrollers,x[:domain],nil)
end
if result['totalentries'] == 0
if dcs.count == 0
print_error("No Domain Controllers found...")
next
end
count = result['totalentries']
startmem = result['bufptr']
base = 0
x[:dc] = []
mem = client.railgun.memread(startmem, 8*count)
count.times{|i|
t = {}
t[:platform] = mem[(base + 0),4].unpack("V*")[0]
nameptr = mem[(base + 4),4].unpack("V*")[0]
t[:dc_hostname] = client.railgun.memread(nameptr,255).split("\0\0")[0].split("\0").join
x[:dc] << t
base = base + 8
print_status(t[:dc_hostname])
dcs.each do |dc|
print_good("Domain Controller: #{dc[:name]}")
report_note(
:host => session,
:type => 'domain.hostnames',
:data => t[:dc_hostname],
:data => dc[:name],
:update => :unique_data
)
}
end
end
end
end