modules/post/windows/gather/enum_shares.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Registry
include Msf::Post::Windows::Priv
SID_PREFIX_USER = 'S-1-5-21-'.freeze
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather SMB Share Enumeration via Registry',
'Description' => %q{ This module will enumerate configured and recently used file shares. },
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>' ],
'Platform' => [ 'win' ],
'SessionTypes' => %w[shell powershell meterpreter],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => []
},
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_registry_open_key
stdapi_registry_check_key_exists
]
}
}
)
)
register_options([
OptBool.new('CURRENT', [ true, 'Enumerate currently configured shares', true]),
OptBool.new('RECENT', [ true, 'Enumerate recently mapped shares', true]),
OptBool.new('ENTERED', [ true, 'Enumerate recently entered UNC Paths in the Run Dialog', true])
])
end
# Convert share type ID `val` to readable string
#
# @return [String] Share type as readable string
def share_type(val)
%w[DISK PRINTER DEVICE IPC SPECIAL TEMPORARY][val] || 'UNKNOWN'
end
# Method for enumerating recent mapped drives on target machine
#
# @return [Array] List of recently mounted UNC paths
def enum_recent_mounts(base_key)
partial_path = base_key + '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer'
explorer_keys = registry_enumkeys(partial_path).to_s || ''
return [] unless explorer_keys.include?('Map Network Drive MRU')
full_path = "#{partial_path}\\Map Network Drive MRU"
vals_found = registry_enumvals(full_path)
return [] unless vals_found
recent_mounts = []
registry_enumvals(full_path).each do |k|
next if k.include?('MRUList')
mounted_path = registry_getvaldata(full_path, k)
recent_mounts << mounted_path if mounted_path.starts_with?('\\\\')
end
recent_mounts
end
# Method for enumerating UNC paths entered in Run dialog box
#
# @return [Array] List of MRU historical UNC paths
def enum_run_unc(base_key)
full_path = base_key + '\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU'
vals_found = registry_enumvals(full_path)
return [] unless vals_found
unc_paths = []
vals_found.each do |k|
next if k.include?('MRUList')
run_entry = registry_getvaldata(full_path, k).to_s
unc_paths << run_entry.gsub(/\\1$/, '') if run_entry.starts_with?('\\\\')
end
unc_paths
end
# Method for enumerating configured shares on a target box
#
# @return [Array] List of network shares in the form of [ name, type, remark, path ]
def enum_conf_shares
shares_key = nil
[
'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\LanmanServer\\Shares',
'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\lanmanserver\\Shares'
].each do |k|
if registry_key_exist?(k)
shares_key = k
break
end
end
if shares_key.blank?
print_status('No network shares were found')
return
end
share_names = registry_enumvals(shares_key)
if share_names.empty?
print_status('No network shares were found')
return
end
shares = []
print_status('The following shares were found:')
share_names.each do |sname|
share_info = registry_getvaldata(shares_key, sname)
next if share_info.nil?
print_status("\tName: #{sname}")
stype = remark = path = nil
share_info.each do |e|
name, val = e.split('=')
case name
when 'Path'
path = val
print_status "\tPath: #{path}"
when 'Type'
stype = share_type(val.to_i)
print_status "\tType: #{stype}"
when 'Remark'
remark = val
print_status("\tRemark: #{remark}") unless remark.blank?
end
end
print_status
# Match the format used by auxiliary/scanner/smb/smb_enumshares
# with an added field for path
shares << [ sname, stype, remark, path ]
end
report_note(
host: session,
type: 'smb.shares',
data: { shares: shares },
update: :unique_data
)
end
def run
unless datastore['CURRENT'] || datastore['RECENT'] || datastore['ENTERED']
fail_with(Failure::BadConfig, 'At least one option (CURRENT, RECENT, ENTERED) must be enabled. Nothing to do.')
end
hostname = sysinfo.nil? ? cmd_exec('hostname') : sysinfo['Computer']
print_status("Running module against #{hostname} (#{session.session_host})")
enum_conf_shares if datastore['CURRENT']
return unless datastore['RECENT'] || datastore['ENTERED']
mount_history = []
run_history = []
if is_system? || is_admin?
mount_history = enum_recent_mounts('HKEY_CURRENT_USER') if datastore['RECENT']
run_history = enum_run_unc('HKEY_CURRENT_USER') if datastore['ENTERED']
else
keys = registry_enumkeys('HKU') || []
keys.each do |maybe_sid|
next unless maybe_sid.starts_with?(SID_PREFIX_USER)
next if maybe_sid.include?('_Classes')
mount_history += enum_recent_mounts("HKU\\#{maybe_sid.chomp}") if datastore['RECENT']
run_history += enum_run_unc("HKU\\#{maybe_sid.chomp}") if datastore['ENTERED']
end
end
unless mount_history.empty?
print_status('Recent mounts found:')
mount_history.each do |i|
print_status("\t#{i}")
end
print_status
end
unless run_history.empty?
print_status('Recent UNC paths entered in Run dialog found:')
run_history.each do |i|
print_status("\t#{i}")
end
print_status
end
end
end