`##
# 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
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation