Land #2107 Post Enum Domain Users
commit
ae3ead6ef9
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue