metasploit-framework/msfvenom

535 lines
14 KiB
Ruby
Executable File

#!/usr/bin/env ruby
# -*- coding: binary -*-
#
# $Id: msfvenom 14909 2012-03-10 06:50:03Z rapid7 $
# $Revision: 14909 $
#
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
require 'fastlib'
require 'msfenv'
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
Status = "[*] "
Error = "[-] "
require 'optparse'
def parse_args
opts = {}
datastore = {}
opt = OptionParser.new
opt.banner = "Usage: #{$0} [options] <var=val>"
opt.separator('')
opt.separator('Options:')
opt.on('-p', '--payload [payload]', String, 'Payload to use. Specify a \'-\' or stdin to use custom payloads') do |p|
if p == '-'
opts[:payload] = 'stdin'
else
opts[:payload] = p
end
end
opt.on('-l', '--list [module_type]', Array, 'List a module type example: payloads, encoders, nops, all') do |l|
if l.nil? or l.empty?
l = ["all"]
end
opts[:list] = l
end
opt.on('-n', '--nopsled [length]', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
opts[:nopsled] = n.to_i
end
opt.on('-f', '--format [format]', String, "Output format (use --help-formats for a list)") do |f|
opts[:format] = f
end
opt.on('-e', '--encoder [encoder]', String, 'The encoder to use') do |e|
opts[:encode] = true
opts[:encoder] = e
end
opt.on('-a', '--arch [architecture]', String, 'The architecture to use') do |a|
opts[:arch] = a
end
opt.on('--platform [platform]', String, 'The platform of the payload') do |l|
opts[:platform] = l
end
opt.on('-s', '--space [length]', Integer, 'The maximum size of the resulting payload') do |s|
opts[:space] = s
end
opt.on('-b', '--bad-chars [list] ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
opts[:badchars] = b
end
opt.on('-i', '--iterations [count] ', Integer, 'The number of times to encode the payload') do |i|
opts[:iterations] = i
end
opt.on('-c', '--add-code [path] ', String, 'Specify an additional win32 shellcode file to include') do |x|
opts[:addshellcode] = x
end
opt.on('-x', '--template [path] ', String, 'Specify a custom executable file to use as a template') do |x|
opts[:template] = x
unless File.exist?(x)
$stderr.puts "Template file (#{x}) does not exist"
exit 1
end
end
opt.on('-k', '--keep', 'Preserve the template behavior and inject the payload as a new thread') do
opts[:inject] = true
end
opt.on('-o', '--options', "List the payload's standard options") do
opts[:list_options] = true
end
opt.on_tail('-h', '--help', 'Show this message') do
$stderr.puts opt
exit(1)
end
opt.on_tail('--help-formats', String, "List available formats") do
require 'rex'
require 'msf/ui'
require 'msf/base'
$framework = Msf::Simple::Framework.create(
:module_types => [],
'DisableDatabase' => true
)
puts "Executable formats"
puts "\t" + Msf::Util::EXE.to_executable_fmt_formats.join(", ")
puts "Transform formats"
puts "\t" + Msf::Simple::Buffer.transform_formats.join(", ")
exit 1
end
begin
opt.parse!
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
puts "Invalid option, try -h for usage"
exit(1)
end
args = ARGV.dup
if args
args.each do |x|
k,v = x.split('=', 2)
datastore[k] = v.to_s
end
end
if opts.empty?
puts "no options"
puts opt
exit(1)
end
if opts[:payload].nil? # if no payload option is selected assume we are reading it from stdin
opts[:payload] = "stdin"
end
return [datastore, opts]
end
def print_status(msg)
$stderr.puts(Status + msg)
end
def print_error(msg)
$stderr.puts(Error + msg)
end
def get_encoders(arch, encoder)
encoders = []
if (encoder)
encoders << $framework.encoders.create(encoder)
else
$framework.encoders.each_module_ranked(
'Arch' => arch ? arch.split(',') : nil) { |name, mod|
encoders << mod.new
}
end
encoders
end
def payload_stdin
$stdin.binmode
payload = $stdin.read
payload
end
def generate_nops(arch, len, nop_mod=nil, opts={})
opts['BadChars'] ||= ''
opts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ]
if nop_mod
nop = $framework.nops.create(nop_mod)
raw = nop.generate_sled(len, opts)
return raw if raw
end
$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 dump_payloads
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Header' => "Framework Payloads (#{$framework.stats.num_payloads} total)",
'Columns' =>
[
"Name",
"Description"
])
$framework.payloads.each_module { |name, mod|
tbl << [ name, mod.new.description ]
}
"\n" + tbl.to_s + "\n"
end
def dump_encoders(arch = nil)
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""),
'Columns' =>
[
"Name",
"Rank",
"Description"
])
cnt = 0
$framework.encoders.each_module(
'Arch' => arch ? arch.split(',') : nil) { |name, mod|
tbl << [ name, mod.rank_to_s, mod.new.name ]
cnt += 1
}
(cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n"
end
def dump_nops
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Header' => "Framework NOPs (#{$framework.stats.num_nops} total)",
'Columns' =>
[
"Name",
"Description"
])
$framework.nops.each_module { |name, mod|
tbl << [ name, mod.new.description ]
}
"\n" + tbl.to_s + "\n"
end
datastore, opts = parse_args
require 'rex'
require 'msf/ui'
require 'msf/base'
$framework ||= Msf::Simple::Framework.create(
:module_types => [Msf::MODULE_PAYLOAD, Msf::MODULE_ENCODER, Msf::MODULE_NOP],
'DisableDatabase' => true
)
if opts[:list]
opts[:list].each do |mod|
case mod
when /^payloads$/i
$stderr.puts dump_payloads
when /^encoders$/i
$stderr.puts dump_encoders(opts[:arch])
when /^nops$/i
$stderr.puts dump_nops
when /^all$/i
$stderr.puts dump_payloads
$stderr.puts dump_encoders
$stderr.puts dump_nops
else
print_error("Invalid module type")
end
end
exit
end
if opts[:payload]
if opts[:payload] == 'stdin'
payload_raw = payload_stdin
if opts[:encode] and (opts[:arch].nil? or opts[:platform].nil?)
print_error("Cannot encode stdin payload without specifying the proper architecture and platform")
opts[:encode] = false
end
else
payload = $framework.payloads.create(opts[:payload])
if payload.nil?
print_error("Invalid payload: #{opts[:payload]}")
exit
end
payload.datastore.merge! datastore
end
end
# Normalize the options
opts[:platform] = Msf::Module::PlatformList.transform(opts[:platform]) if opts[:platform]
opts[:badchars] = Rex::Text.hex_to_raw(opts[:badchars]) if opts[:badchars]
# set the defaults unless something is already set by the user
if opts[:payload] != 'stdin'
opts[:arch] ||= payload.arch[0]
# If it's not stdin, we'll already have a PlatfromList
opts[:platform] ||= payload.platform
else
# defaults for stdin payloads users should define them
unless opts[:arch]
print_error("Defaulting to x86 architecture for stdin payload, use -a to change")
opts[:arch] = "x86"
end
unless opts[:platform]
print_error("Defaulting to Windows platform for stdin payload, use --platform to change")
opts[:platform] = Msf::Module::PlatformList.transform("Windows")
end
end
# After this point, we will have set a platform, even if it's wrong.
opts[:format] ||= 'ruby'
opts[:encoder] ||= nil
opts[:encode] ||= !(opts[:badchars].nil? or opts[:badchars].empty?)
if opts[:encoder].nil?
fmt = 'raw'
else
fmt = 'raw'
encoders = get_encoders(opts[:arch], opts[:encoder])
end
if opts[:list_options]
print_status("Options for #{payload.fullname}\n\n" +
::Msf::Serializer::ReadableText.dump_options(payload,' '))
exit
end
if payload_raw.nil? or payload_raw.empty?
begin
payload_raw = payload.generate_simple(
'Format' => fmt,
'Options' => datastore,
'Encoder' => nil)
rescue
$stderr.puts "Error generating payload: #{$!}"
exit
end
end
if opts[:template]
path = File.dirname(opts[:template])
altexe = File.basename(opts[:template])
end
exeopts = { :inject => opts[:inject], :template_path => path, :template => altexe }
# If we were given addshellcode for a win32 payload,
# create a double-payload; one running in one thread, one running in the other
if opts[:addshellcode] and opts[:platform].platforms.include?(Msf::Module::Platform::Windows) and opts[:arch] == 'x86'
payload_raw = Msf::Util::EXE.win32_rwx_exec_thread(payload_raw,0,'end')
file = ::File.new(opts[:addshellcode])
file.binmode
payload_raw << file.read
file.close
end
if opts[:encode]
done = false
encoders = get_encoders(opts[:arch], opts[:encoder])
encoders.each do |enc|
next if not enc
begin
break if done
enc.datastore.import_options_from_hash(datastore)
skip = false
raw = nil
if not opts[:iterations]
opts[:iterations] = 1
end
1.upto(opts[:iterations].to_i) do |iteration|
begin
raw = enc.encode(payload_raw.dup, opts[:badchars], nil, opts[:platform])
rescue Msf::EncodingError
print_error("#{enc.refname} failed: #{$!.class} : #{$!}")
skip = true
break
end
if opts[:space] and opts[:space] > 0 and raw.length > opts[:space]
print_error("#{enc.refname} created buffer that is too big (#{raw.length})\n\n")
skip = true
break
end
print_status("#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n")
payload_raw = raw.dup
if iteration == opts[:iterations]
done = true
break
end
end
next if skip
rescue ::Errno::ENOENT, ::Errno::EINVAL
print_error("#{enc.refname} failed: #{$!}")
break
rescue => e
print_error("#{enc.refname} failed: #{e.class} #{e}")
e.backtrace.each { |el|
$stderr.puts(el.to_s)
}
end
end
end
if opts[:nopsled]
#puts opts[:arch].class
nopts = { 'BadChars' => opts[:badchars] }
nops = generate_nops([opts[:arch]], opts[:nopsled], nil, nopts)
payload_raw = nops + payload_raw
end
$stdout.binmode
if opts[:format] !~/^(ruby|rb|perl|pl|bash|sh|c|csharp|js|dll|elf)$/i
exe = Msf::Util::EXE.to_executable_fmt($framework, opts[:arch], opts[:platform], payload_raw, opts[:format], exeopts)
end
case opts[:format]
when /^(ruby|rb|perl|pl|bash|sh|c|csharp|js_le|raw|py)$/i
$stdout.write Msf::Simple::Buffer.transform(payload_raw, opts[:format])
when /^asp$/
asp = Msf::Util::EXE.to_win32pe_asp($framework, payload_raw, exeopts)
$stdout.puts asp
when /^aspx$/
aspx = Msf::Util::EXE.to_win32pe_aspx($framework, payload_raw, exeopts)
$stdout.puts aspx
when /^js_be$/i
if Rex::Arch.endian(payload.arch) != ENDIAN_BIG
print_error("Big endian format selected for a non big endian payload")
exit
end
$stdout.puts Msf::Simple::Buffer.transform(payload_raw, opts[:format])
when /^java$/i
if(!exe and payload.platform.platforms.index(Msf::Module::Platform::Java))
exe = payload.generate_jar.pack
end
if exe
$stdout.write exe
else
print_error("Could not generate payload format")
end
when /^elf$/i
if (opts[:platform].index(Msf::Module::Platform::Linux))
elf = case opts[:arch]
when /^x64$/; Msf::Util::EXE.to_linux_x64_elf($framework, payload_raw, exeopts)
when /^x86$/; Msf::Util::EXE.to_linux_x86_elf($framework, payload_raw, exeopts)
when /^arm$/; Msf::Util::EXE.to_linux_armle_elf($framework, payload_raw, exeopts)
end
elsif(opts[:platform].index(Msf::Module::Platform::BSD))
elf = case opts[:arch]
when /^x86$/; Msf::Util::EXE.to_bsd_x86_elf($framework, payload_raw, exeopts)
end
elsif(opts[:platform].index(Msf::Module::Platform::Solaris))
elf = case opts[:arch]
when /^x86$/; Msf::Util::EXE.to_solaris_x86_elf($framework, payload_raw, exeopts)
end
end
if elf.nil?
print_error("This format does not support that architecture")
exit
end
$stdout.write elf
when /^macho$/i
bin = case opts[:arch]
when /^x64$/; Msf::Util::EXE.to_osx_x64_macho($framework, payload_raw, exeopts)
when /^x86$/; Msf::Util::EXE.to_osx_x86_macho($framework, payload_raw, exeopts)
when /^arm$/; Msf::Util::EXE.to_osx_arm_macho($framework, payload_raw, exeopts)
when /^ppc$/; Msf::Util::EXE.to_osx_ppc_macho($framework, payload_raw, exeopts)
end
if bin.nil?
print_error("This format does not support that architecture")
exit
end
$stdout.write bin
when /^dll$/i
dll = case opts[:arch]
when /^x86$/; Msf::Util::EXE.to_win32pe_dll($framework, payload_raw)
when /^(x64|x86_64)$/; Msf::Util::EXE.to_win64pe_dll($framework, payload_raw)
end
if dll.nil?
print_error("This format does not support that architecture")
exit
end
$stdout.write dll
when /^exe$/i
$stdout.write exe
when /^exe-small$/i
when /^vba$/i
vba = Msf::Util::EXE.to_vba($framework, payload_raw)
$stdout.puts vba
when /^vba-exe$/i
exe = Msf::Util::EXE.to_win32pe($framework, payload_raw)
vba = Msf::Util::EXE.to_exe_vba(exe)
$stdout.puts vba
when /^vbs$/i
exe = Msf::Util::EXE.to_win32pe($framework, payload_raw)
vbs = Msf::Util::EXE.to_exe_vbs(exe)
$stdout.puts vbs
when /^war$/i
if (!exe and payload.platform.platforms.index(Msf::Module::Platform::Java))
exe = payload.generate_war.pack
else
exe = Msf::Util::EXE.to_jsp_war(exe)
end
$stdout.write exe
when /^psh$/i
psh = Msf::Util::EXE.to_win32pe_psh($framework, payload_raw, exeopts)
$stdout.write psh
when /^psh-net$/i
psh = Msf::Util::EXE.to_win32pe_psh_net($framework, payload_raw, exeopts)
$stdout.write psh
else
print_error("Unsupported format")
exit
end