metasploit-framework/modules/auxiliary/scanner/smb/smb_enumusers_domain.rb

168 lines
4.2 KiB
Ruby

##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
# Exploit mixins should be called first
include Msf::Exploit::Remote::SMB
include Msf::Exploit::Remote::SMB::Authenticated
include Msf::Exploit::Remote::DCERPC
# Scanner mixin should be near last
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'SMB Domain User Enumeration',
'Version' => '$Revision $',
'Description' => 'Determine what domain users are logged into a remote system via a DCERPC to NetWkstaUserEnum.',
'Author' => 'natron',
'Version' => '$Revision$',
'References' =>
[
[ 'URL', 'http://msdn.microsoft.com/en-us/library/aa370669%28VS.85%29.aspx' ]
],
'License' => MSF_LICENSE
)
deregister_options('RPORT', 'RHOST')
register_advanced_options(
[
OptBool.new('VERBOSE', [ false, "Print logon server and additional domains identified.", false ])
], self.class)
end
def parse_value(resp, idx)
#val_length = resp[idx,4].unpack("V")[0]
idx += 4
#val_offset = resp[idx,4].unpack("V")[0]
idx += 4
val_actual = resp[idx,4].unpack("V")[0]
idx += 4
value = resp[idx,val_actual*2]
#print_debug "resp[0x#{idx.to_s(16)},#{val_actual*2}] : " + value
idx += val_actual * 2
idx += val_actual % 2 * 2 # alignment
return value,idx
end
def parse_NetWkstaEnumUsersInfo(resp)
accounts = [ Hash.new() ]
#print_debug resp[0,20].unpack("H*")
idx = 20
count = resp[idx,4].unpack("V")[0] # wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Max Count
idx += 4
#print_debug "Max Count : " + count.to_s
1.upto(count) do
# wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Ref ID
# print_debug "Ref ID#{account.to_s}: " + resp[idx,4].unpack("H*").to_s
idx += 4 # ref id name
idx += 4 # ref id logon domain
idx += 4 # ref id other domains
idx += 4 # ref id logon server
end
1.upto(count) do
# wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> ID1 max count
account_name,idx = parse_value(resp, idx)
logon_domain,idx = parse_value(resp, idx)
other_domains,idx = parse_value(resp, idx)
logon_server,idx = parse_value(resp, idx)
accounts << {
:account_name => account_name,
:logon_domain => logon_domain,
:other_domains => other_domains,
:logon_server => logon_server
}
end
accounts
end
def run_host(ip)
[[139, false], [445, true]].each do |info|
datastore['RPORT'] = info[0]
datastore['SMBDirect'] = info[1]
begin
connect()
smb_login()
uuid = [ '6bffd098-a112-3610-9833-46c3f87e345a', '1.0' ]
handle = dcerpc_handle(
uuid[0], uuid[1], 'ncacn_np', ["\\wkssvc"]
)
begin
dcerpc_bind(handle)
stub =
NDR.uwstring("\\\\" + ip) + # Server Name
NDR.long(1) + # Level
NDR.long(1) + # Ctr
NDR.long(rand(0xffffffff)) + # ref id
NDR.long(0) + # entries read
NDR.long(0) + # null ptr to user0
NDR.long(0xffffffff) + # Prefmaxlen
NDR.long(rand(0xffffffff)) + # ref id
NDR.long(0) # null ptr to resume handle
dcerpc.call(2,stub)
resp = dcerpc.last_response ? dcerpc.last_response.stub_data : nil
accounts = parse_NetWkstaEnumUsersInfo(resp)
accounts.shift
if datastore['VERBOSE']
accounts.each do |x|
print_status ip + " : " + x[:logon_domain] + "\\" + x[:account_name] +
"\t(logon_server: #{x[:logon_server]}, other_domains: #{x[:other_domains]})"
end
else
print_status "#{ip} : #{accounts.collect{|x| x[:logon_domain] + "\\" + x[:account_name]}.join(", ")}"
end
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
print_line("UUID #{uuid[0]} #{uuid[1]} ERROR 0x%.8x" % e.error_code)
#puts e
#return
rescue ::Exception => e
print_line("UUID #{uuid[0]} #{uuid[1]} ERROR #{$!}")
#puts e
#return
end
disconnect()
return
rescue ::Exception
print_line($!.to_s)
end
end
end
end