diff --git a/msfvenom b/msfvenom index 36a8f7be05..6c25288081 100755 --- a/msfvenom +++ b/msfvenom @@ -1,365 +1,361 @@ #!/usr/bin/env ruby # -*- coding: binary -*- -if __FILE__ == $0 +msfbase = __FILE__ +while File.symlink?(msfbase) + msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) +end - msfbase = __FILE__ - while File.symlink?(msfbase) - msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) +$:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) +require 'msfenv' + +$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] + +require 'rex' +require 'msf/ui' +require 'msf/base' +require 'msf/core/payload_generator' + + +class MsfVenomError < StandardError; end +class UsageError < MsfVenomError; end +class NoTemplateError < MsfVenomError; end +class IncompatibleError < MsfVenomError; end + + +require 'optparse' + + +# Creates a new framework object. +# +# @note Ignores any previously cached value. +# @param (see ::Msf::Simple::Framework.create) +# @return [Msf::Framework] +def init_framework(create_opts={}) + create_opts[:module_types] ||= [ + ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP + ] + @framework = ::Msf::Simple::Framework.create(create_opts.merge('DisableDatabase' => true)) +end + +# Cached framework object +# +# @return [Msf::Framework] +def framework + return @framework if @framework + + init_framework + + @framework +end + + +def parse_args(args) + opts = {} + datastore = {} + opt = OptionParser.new + banner = "MsfVenom - a Metasploit standalone payload generator.\n" + banner << "Also a replacement for msfpayload and msfencode.\n" + banner << "Usage: #{$0} [options] " + opt.banner = banner + opt.separator('') + opt.separator('Options:') + + opt.on('-p', '--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 - $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) - require 'msfenv' - - $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] - - require 'rex' - require 'msf/ui' - require 'msf/base' - require 'msf/core/payload_generator' - - - class MsfVenomError < StandardError; end - class UsageError < MsfVenomError; end - class NoTemplateError < MsfVenomError; end - class IncompatibleError < MsfVenomError; end - - - require 'optparse' - - - # Creates a new framework object. - # - # @note Ignores any previously cached value. - # @param (see ::Msf::Simple::Framework.create) - # @return [Msf::Framework] - def init_framework(create_opts={}) - create_opts[:module_types] ||= [ - ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP - ] - @framework = ::Msf::Simple::Framework.create(create_opts.merge('DisableDatabase' => true)) + opt.on('--payload-options', "List the payload's standard options") do + opts[:list_options] = true end - # Cached framework object - # - # @return [Msf::Framework] - def framework - return @framework if @framework - - init_framework - - @framework + opt.on('-l', '--list [type]', Array, 'List a module type. Options are: payloads, encoders, nops, all') do |l| + if l.nil? or l.empty? + l = ["all"] + end + opts[:list] = l end + opt.on('-n', '--nopsled ', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n| + opts[:nops] = n.to_i + end - def parse_args(args) - opts = {} - datastore = {} - opt = OptionParser.new - banner = "MsfVenom - a Metasploit standalone payload generator.\n" - banner << "Also a replacement for msfpayload and msfencode.\n" - banner << "Usage: #{$0} [options] " - opt.banner = banner - opt.separator('') - opt.separator('Options:') + opt.on('-f', '--format ', String, "Output format (use --help-formats for a list)") do |f| + opts[:format] = f + end - opt.on('-p', '--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 + opt.on('--help-formats', String, "List available formats") do + init_framework(:module_types => []) + msg = "Executable formats\n" + + "\t" + ::Msf::Util::EXE.to_executable_fmt_formats.join(", ") + "\n" + + "Transform formats\n" + + "\t" + ::Msf::Simple::Buffer.transform_formats.join(", ") + raise UsageError, msg + end + + opt.on('-e', '--encoder ', String, 'The encoder to use') do |e| + opts[:encoder] = e + end + + opt.on('-a', '--arch ', String, 'The architecture to use') do |a| + opts[:arch] = a + end + + opt.on('--platform ', String, 'The platform of the payload') do |l| + opts[:platform] = l + end + + opt.on('--help-platforms', String, 'List available platforms') do + init_framework(:module_types => []) + supported_platforms = [] + Msf::Module::Platform.subclasses.each {|c| supported_platforms << "#{c.realname.downcase}"} + msg = "Platforms\n" + + "\t" + supported_platforms * ", " + raise UsageError, msg + end + + opt.on('-s', '--space ', Integer, 'The maximum size of the resulting payload') do |s| + opts[:space] = s + end + + opt.on('--encoder-space ', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s| + opts[:encoder_space] = s + end + + opt.on('-b', '--bad-chars ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b| + opts[:badchars] = Rex::Text.hex_to_raw(b) + end + + opt.on('-i', '--iterations ', Integer, 'The number of times to encode the payload') do |i| + opts[:iterations] = i + end + + opt.on('-c', '--add-code ', String, 'Specify an additional win32 shellcode file to include') do |x| + opts[:add_code] = x + end + + opt.on('-x', '--template ', String, 'Specify a custom executable file to use as a template') do |x| + opts[:template] = x + end + + opt.on('-k', '--keep', 'Preserve the template behavior and inject the payload as a new thread') do + opts[:keep] = true + end + + opt.on('-o', '--out ', 'Save the payload') do |x| + opts[:out] = x + end + + opt.on('-v', '--var-name ', String, 'Specify a custom variable name to use for certain output formats') do |x| + opts[:var_name] = x + end + + opt.on('--smallest', 'Generate the smallest possible payload') do + opts[:smallest] = true + end + + opt.on_tail('-h', '--help', 'Show this message') do + raise UsageError, "#{opt}" + end + + begin + opt.parse!(args) + rescue OptionParser::InvalidOption => e + raise UsageError, "Invalid option\n#{opt}" + rescue OptionParser::MissingArgument => e + raise UsageError, "Missing required argument for option\n#{opt}" + end + + if opts.empty? + raise UsageError, "No options\n#{opt}" + end + + if args + args.each do |x| + k,v = x.split('=', 2) + datastore[k.upcase] = v.to_s end - - opt.on('--payload-options', "List the payload's standard options") do - opts[:list_options] = true + if opts[:payload].to_s =~ /[\_\/]reverse/ and datastore['LHOST'].nil? + datastore['LHOST'] = Rex::Socket.source_address end + end - opt.on('-l', '--list [type]', Array, 'List a module type. Options are: payloads, encoders, nops, all') do |l| - if l.nil? or l.empty? - l = ["all"] - end - opts[:list] = l - end - - opt.on('-n', '--nopsled ', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n| - opts[:nops] = n.to_i - end - - opt.on('-f', '--format ', String, "Output format (use --help-formats for a list)") do |f| - opts[:format] = f - end - - opt.on('--help-formats', String, "List available formats") do - init_framework(:module_types => []) - msg = "Executable formats\n" + - "\t" + ::Msf::Util::EXE.to_executable_fmt_formats.join(", ") + "\n" + - "Transform formats\n" + - "\t" + ::Msf::Simple::Buffer.transform_formats.join(", ") - raise UsageError, msg - end - - opt.on('-e', '--encoder ', String, 'The encoder to use') do |e| - opts[:encoder] = e - end - - opt.on('-a', '--arch ', String, 'The architecture to use') do |a| - opts[:arch] = a - end - - opt.on('--platform ', String, 'The platform of the payload') do |l| - opts[:platform] = l - end - - opt.on('--help-platforms', String, 'List available platforms') do - init_framework(:module_types => []) - supported_platforms = [] - Msf::Module::Platform.subclasses.each {|c| supported_platforms << "#{c.realname.downcase}"} - msg = "Platforms\n" + - "\t" + supported_platforms * ", " - raise UsageError, msg - end - - opt.on('-s', '--space ', Integer, 'The maximum size of the resulting payload') do |s| - opts[:space] = s - end - - opt.on('--encoder-space ', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s| - opts[:encoder_space] = s - end - - opt.on('-b', '--bad-chars ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b| - opts[:badchars] = Rex::Text.hex_to_raw(b) - end - - opt.on('-i', '--iterations ', Integer, 'The number of times to encode the payload') do |i| - opts[:iterations] = i - end - - opt.on('-c', '--add-code ', String, 'Specify an additional win32 shellcode file to include') do |x| - opts[:add_code] = x - end - - opt.on('-x', '--template ', String, 'Specify a custom executable file to use as a template') do |x| - opts[:template] = x - end - - opt.on('-k', '--keep', 'Preserve the template behavior and inject the payload as a new thread') do - opts[:keep] = true - end - - opt.on('-o', '--out ', 'Save the payload') do |x| - opts[:out] = x - end - - opt.on('-v', '--var-name ', String, 'Specify a custom variable name to use for certain output formats') do |x| - opts[:var_name] = x - end - - opt.on('--smallest', 'Generate the smallest possible payload') do - opts[:smallest] = true - end - - opt.on_tail('-h', '--help', 'Show this message') do - raise UsageError, "#{opt}" - end + if opts[:payload].nil? # if no payload option is selected assume we are reading it from stdin + opts[:payload] = "stdin" + end + if opts[:payload] == 'stdin' and not opts[:list] + $stderr.puts "Attempting to read payload from STDIN..." begin - opt.parse!(args) - rescue OptionParser::InvalidOption => e - raise UsageError, "Invalid option\n#{opt}" - rescue OptionParser::MissingArgument => e - raise UsageError, "Missing required argument for option\n#{opt}" - end - - if opts.empty? - raise UsageError, "No options\n#{opt}" - end - - if args - args.each do |x| - k,v = x.split('=', 2) - datastore[k.upcase] = v.to_s - end - if opts[:payload].to_s =~ /[\_\/]reverse/ and datastore['LHOST'].nil? - datastore['LHOST'] = Rex::Socket.source_address + ::Timeout.timeout(30) do + opts[:stdin] = payload_stdin end + rescue Timeout::Error + opts[:stdin] = '' end - - if opts[:payload].nil? # if no payload option is selected assume we are reading it from stdin - opts[:payload] = "stdin" - end - - if opts[:payload] == 'stdin' and not opts[:list] - $stderr.puts "Attempting to read payload from STDIN..." - begin - ::Timeout.timeout(30) do - opts[:stdin] = payload_stdin - end - rescue Timeout::Error - opts[:stdin] = '' - end - end - - opts[:datastore] = datastore - - opts end + opts[:datastore] = datastore - # Read a raw payload from stdin (or whatever IO object we're currently - # using as stdin, see {#initialize}) - # - # @return [String] - def payload_stdin - @in = $stdin - @in.binmode - payload = @in.read - payload - end + opts +end - def dump_payloads - init_framework(:module_types => [ ::Msf::MODULE_PAYLOAD ]) - 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.split.join(' ') ] - } +# Read a raw payload from stdin (or whatever IO object we're currently +# using as stdin, see {#initialize}) +# +# @return [String] +def payload_stdin + @in = $stdin + @in.binmode + payload = @in.read + payload +end - "\n" + tbl.to_s + "\n" - end +def dump_payloads + init_framework(:module_types => [ ::Msf::MODULE_PAYLOAD ]) + tbl = Rex::Ui::Text::Table.new( + 'Indent' => 4, + 'Header' => "Framework Payloads (#{framework.stats.num_payloads} total)", + 'Columns' => + [ + "Name", + "Description" + ]) - def dump_encoders(arch = nil) - init_framework(:module_types => [ ::Msf::MODULE_ENCODER ]) - tbl = Rex::Ui::Text::Table.new( - 'Indent' => 4, - 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""), - 'Columns' => - [ - "Name", - "Rank", - "Description" - ]) - cnt = 0 + framework.payloads.each_module { |name, mod| + tbl << [ name, mod.new.description.split.join(' ') ] + } - framework.encoders.each_module( - 'Arch' => arch ? arch.split(',') : nil) { |name, mod| + "\n" + tbl.to_s + "\n" +end + +def dump_encoders(arch = nil) + init_framework(:module_types => [ ::Msf::MODULE_ENCODER ]) + 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 +end - def dump_nops - init_framework(:module_types => [ ::Msf::MODULE_NOP ]) - tbl = Rex::Ui::Text::Table.new( - 'Indent' => 4, - 'Header' => "Framework NOPs (#{framework.stats.num_nops} total)", - 'Columns' => - [ - "Name", - "Description" - ]) +def dump_nops + init_framework(:module_types => [ ::Msf::MODULE_NOP ]) + 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.split.join(' ') ] - } + framework.nops.each_module { |name, mod| + tbl << [ name, mod.new.description.split.join(' ') ] + } - "\n" + tbl.to_s + "\n" - end + "\n" + tbl.to_s + "\n" +end - begin - generator_opts = parse_args(ARGV) - rescue MsfVenomError, Msf::OptionValidateError => e - $stderr.puts "Error: #{e.message}" - exit(1) - end +begin + generator_opts = parse_args(ARGV) +rescue MsfVenomError, Msf::OptionValidateError => e + $stderr.puts "Error: #{e.message}" + exit(1) +end - if generator_opts[:list] - generator_opts[:list].each do |mod| - case mod.downcase - when "payloads", "payload", "p" - $stdout.puts dump_payloads - when "encoders", "encoder", "e" - $stdout.puts dump_encoders(generator_opts[:arch]) - when "nops", "nop", "n" - $stdout.puts dump_nops - when "all" - # Init here so #dump_payloads doesn't create a framework with - # only payloads, etc. - init_framework - $stdout.puts dump_payloads - $stdout.puts dump_encoders - $stdout.puts dump_nops - else - $stderr.puts "Invalid module type. These are valid: payloads, encoders, nops, all" - end +if generator_opts[:list] + generator_opts[:list].each do |mod| + case mod.downcase + when "payloads", "payload", "p" + $stdout.puts dump_payloads + when "encoders", "encoder", "e" + $stdout.puts dump_encoders(generator_opts[:arch]) + when "nops", "nop", "n" + $stdout.puts dump_nops + when "all" + # Init here so #dump_payloads doesn't create a framework with + # only payloads, etc. + init_framework + $stdout.puts dump_payloads + $stdout.puts dump_encoders + $stdout.puts dump_nops + else + $stderr.puts "Invalid module type. These are valid: payloads, encoders, nops, all" end - exit(0) + end + exit(0) +end + +if generator_opts[:list_options] + payload_mod = framework.payloads.create(generator_opts[:payload]) + + if payload_mod.nil? + $stderr.puts "Invalid payload: #{generator_opts[:payload]}" + exit end - if generator_opts[:list_options] - payload_mod = framework.payloads.create(generator_opts[:payload]) + $stderr.puts "Options for #{payload_mod.fullname}:\n\n" + $stdout.puts ::Msf::Serializer::ReadableText.dump_module(payload_mod, ' ') - if payload_mod.nil? - $stderr.puts "Invalid payload: #{generator_opts[:payload]}" - exit - end + $stderr.puts "Advanced options for #{payload_mod.fullname}:\n\n" + $stdout.puts ::Msf::Serializer::ReadableText.dump_advanced_options(payload_mod, ' ') - $stderr.puts "Options for #{payload_mod.fullname}:\n\n" - $stdout.puts ::Msf::Serializer::ReadableText.dump_module(payload_mod, ' ') + $stderr.puts "Evasion options for #{payload_mod.fullname}:\n\n" + $stdout.puts ::Msf::Serializer::ReadableText.dump_evasion_options(payload_mod, ' ') + exit(0) +end - $stderr.puts "Advanced options for #{payload_mod.fullname}:\n\n" - $stdout.puts ::Msf::Serializer::ReadableText.dump_advanced_options(payload_mod, ' ') +generator_opts[:framework] = framework +generator_opts[:cli] = true - $stderr.puts "Evasion options for #{payload_mod.fullname}:\n\n" - $stdout.puts ::Msf::Serializer::ReadableText.dump_evasion_options(payload_mod, ' ') - exit(0) - end +begin + venom_generator = Msf::PayloadGenerator.new(generator_opts) + payload = venom_generator.generate_payload +rescue ::Exception => e + elog("#{e.class} : #{e.message}\n#{e.backtrace * "\n"}") + $stderr.puts "Error: #{e.message}" +end - generator_opts[:framework] = framework - generator_opts[:cli] = true +# No payload generated, no point to go on +exit(2) unless payload +if generator_opts[:out] begin - venom_generator = Msf::PayloadGenerator.new(generator_opts) - payload = venom_generator.generate_payload + ::File.open(generator_opts[:out], 'wb') do |f| + f.write(payload) + end + $stderr.puts "Saved as: #{generator_opts[:out]}" rescue ::Exception => e + # If I can't save it, then I can't save it. I don't think it matters what error. elog("#{e.class} : #{e.message}\n#{e.backtrace * "\n"}") $stderr.puts "Error: #{e.message}" end - - # No payload generated, no point to go on - exit(2) unless payload - - if generator_opts[:out] - begin - ::File.open(generator_opts[:out], 'wb') do |f| - f.write(payload) - end - $stderr.puts "Saved as: #{generator_opts[:out]}" - rescue ::Exception => e - # If I can't save it, then I can't save it. I don't think it matters what error. - elog("#{e.class} : #{e.message}\n#{e.backtrace * "\n"}") - $stderr.puts "Error: #{e.message}" - end - else - output_stream = $stdout - output_stream.binmode - output_stream.write payload - # trailing newline for pretty output - $stderr.puts unless payload =~ /\n$/ - end - +else + output_stream = $stdout + output_stream.binmode + output_stream.write payload + # trailing newline for pretty output + $stderr.puts unless payload =~ /\n$/ end