Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

544 lines
15 KiB
Ruby
Raw Permalink Normal View History

#!/usr/bin/env ruby
# -*- coding: binary -*-
2025-07-04 14:02:20 -04:00
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('Gemfile', __dir__)
require 'bundler/setup'
2016-02-12 10:49:18 -06:00
class MsfVenomError < StandardError; end
2016-05-09 21:25:23 +02:00
class HelpError < StandardError; end
2016-02-12 10:49:18 -06:00
class UsageError < MsfVenomError; end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
require 'optparse'
2018-03-17 01:30:54 -05:00
require 'timeout'
2013-07-01 17:15:48 -05:00
2020-05-05 12:34:44 +01:00
# Silences warnings as they only serve to confuse end users
if defined?(Warning) && Warning.respond_to?(:[]=)
Warning[:deprecated] = false
end
2020-03-12 17:58:26 +00:00
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')))
2020-03-12 17:58:26 +00:00
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'
2021-01-05 16:42:59 +00:00
require 'msf/core/constants'
unless $stdout.tty?
Rex::Text::Table.unwrap_tables!
end
@framework_loaded = true
end
2016-02-12 10:49:18 -06:00
# 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
2016-02-12 10:49:18 -06:00
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)
2016-02-12 10:49:18 -06:00
end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
# Cached framework object
#
# @return [Msf::Framework]
def framework
return @framework if @framework
2013-07-02 01:58:08 -05:00
2016-02-12 10:49:18 -06:00
init_framework
2013-07-02 01:58:08 -05:00
2016-02-12 10:49:18 -06:00
@framework
end
2013-07-02 01:58:08 -05:00
2016-02-12 10:49:18 -06:00
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"
2018-04-10 11:14:14 -05:00
banner << "Usage: #{$0} [options] <var=val>\n"
banner << "Example: #{$0} -p windows/meterpreter/reverse_tcp LHOST=<IP> -f exe -o payload.exe"
2016-02-12 10:49:18 -06:00
opt.banner = banner
opt.separator('')
opt.separator('Options:')
2018-08-24 14:33:36 -05:00
opt.on('-l', '--list <type>', Array, 'List all modules for [type]. Types are: payloads, encoders, nops, platforms, archs, encrypt, formats, all') do |l|
2018-06-01 18:33:07 -05:00
if l.to_s.empty?
l = ["all"]
end
opts[:list] = l
end
2018-04-10 11:14:14 -05:00
opt.on('-p', '--payload <payload>', String,
2018-05-24 07:34:19 +01:00
"Payload to use (--list payloads to list, --list-options for arguments). Specify '-' or STDIN for custom") do |p|
2016-02-12 10:49:18 -06:00
if p == '-'
opts[:payload] = 'stdin'
else
opts[:payload] = p
2013-07-01 17:15:48 -05:00
end
2016-02-12 10:49:18 -06:00
end
2013-07-01 17:15:48 -05:00
2018-05-24 07:34:19 +01:00
opt.on('--list-options', "List --payload <value>'s standard, advanced and evasion options") do
2016-02-12 10:49:18 -06:00
opts[:list_options] = true
end
2015-05-18 16:39:52 -05:00
opt.on('-f', '--format <format>', String, "Output format (use --list formats to list)") do |f|
opts[:format] = f.downcase
2016-02-12 10:49:18 -06:00
end
2013-07-01 17:15:48 -05:00
opt.on('-e', '--encoder <encoder>', String, 'The encoder to use (use --list encoders to list)') do |e|
opts[:encoder] = e
2016-02-12 10:49:18 -06:00
end
2015-05-18 16:39:52 -05:00
opt.on('--service-name <value>', String, 'The service name to use when generating a service binary') do |s|
2020-06-10 14:52:38 -05:00
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.
2020-06-10 14:52:38 -05:00
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
2018-04-10 11:14:14 -05:00
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
2018-04-10 11:14:14 -05:00
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)
2018-04-10 11:14:14 -05:00
end
opt.on('--encrypt-iv <value>', String, 'An initialization vector for --encrypt') do |e|
2018-04-10 11:14:14 -05:00
opts[:encryption_iv] = e
end
2018-08-24 14:33:36 -05:00
opt.on('-a', '--arch <arch>', String, 'The architecture to use for --payload and --encoders (use --list archs to list)') do |a|
2016-02-12 10:49:18 -06:00
opts[:arch] = a
end
2013-07-01 17:15:48 -05:00
opt.on('--platform <platform>', String, 'The platform for --payload (use --list platforms to list)') do |l|
2016-02-12 10:49:18 -06:00
opts[:platform] = l
end
2013-07-01 17:15:48 -05:00
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
2016-02-12 10:49:18 -06:00
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
2018-04-10 11:14:14 -05:00
opt.on('-s', '--space <length>', Integer, 'The maximum size of the resulting payload') do |s|
2016-02-12 10:49:18 -06:00
opts[:space] = s
end
2013-07-01 17:15:48 -05:00
2018-04-10 11:14:14 -05:00
opt.on('--encoder-space <length>', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
2016-02-12 10:49:18 -06:00
opts[:encoder_space] = s
end
2018-04-10 11:14:14 -05:00
opt.on('-i', '--iterations <count>', Integer, 'The number of times to encode the payload') do |i|
2016-02-12 10:49:18 -06:00
opts[:iterations] = i
end
2013-07-01 17:15:48 -05:00
2018-04-10 11:14:14 -05:00
opt.on('-c', '--add-code <path>', String, 'Specify an additional win32 shellcode file to include') do |x|
2016-02-12 10:49:18 -06:00
opts[:add_code] = x
end
2013-07-01 17:15:48 -05:00
2018-04-10 11:14:14 -05:00
opt.on('-x', '--template <path>', String, 'Specify a custom executable file to use as a template') do |x|
2016-02-12 10:49:18 -06:00
opts[:template] = x
end
2013-07-01 17:15:48 -05:00
opt.on('-k', '--keep', 'Preserve the --template behaviour and inject the payload as a new thread') do
2016-02-12 10:49:18 -06:00
opts[:keep] = true
end
2013-07-01 17:15:48 -05:00
opt.on('-v', '--var-name <value>', String, 'Specify a custom variable name to use for certain output formats') do |x|
2016-02-12 10:49:18 -06:00
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|
2018-04-13 07:31:34 -04:00
opts[:timeout] = x
2018-04-13 07:16:11 -04:00
end
opt.on('--refresh-cache', 'Rebuild the module metadata cache from disk before listing') do
opts[:refresh_cache] = true
end
2016-02-12 10:49:18 -06:00
opt.on_tail('-h', '--help', 'Show this message') do
2016-05-09 21:25:23 +02:00
raise HelpError, "#{opt}"
2016-02-12 10:49:18 -06:00
end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
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
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
if opts.empty?
raise UsageError, "No options\n#{opt}"
end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
if args
args.each do |x|
k,v = x.split('=', 2)
datastore[k.upcase] = v.to_s
2013-07-01 17:15:48 -05:00
end
2018-05-24 07:34:19 +01:00
if opts[:payload].to_s =~ /[\_\/]reverse/ && datastore['LHOST'].nil?
init_framework
2016-02-12 10:49:18 -06:00
datastore['LHOST'] = Rex::Socket.source_address
2013-07-01 17:15:48 -05:00
end
2016-02-12 10:49:18 -06:00
end
if opts[:payload].nil? # if no payload option is selected assume we are reading it from stdin
opts[:payload] = "stdin"
end
2013-07-01 17:15:48 -05:00
if opts[:payload].downcase == 'stdin' && !opts[:list] && !opts[:refresh_cache]
2016-02-12 10:49:18 -06:00
$stderr.puts "Attempting to read payload from STDIN..."
begin
2018-04-13 07:31:34 -04:00
opts[:timeout] ||= 30
2018-04-13 07:16:11 -04:00
::Timeout.timeout(opts[:timeout]) do
2016-02-12 10:49:18 -06:00
opts[:stdin] = payload_stdin
2014-02-05 15:25:14 -06:00
end
2016-02-12 10:49:18 -06:00
rescue Timeout::Error
opts[:stdin] = ''
2013-07-01 17:15:48 -05:00
end
end
2016-02-12 10:49:18 -06:00
opts[:datastore] = datastore
2014-02-05 15:25:14 -06:00
2016-02-12 10:49:18 -06:00
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
2013-07-01 17:15:48 -05:00
def dump_platforms
require_deps unless @framework_loaded
supported_platforms = []
2018-05-24 07:34:19 +01:00
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|
2018-08-23 21:53:49 -05:00
tbl << [name]
end
"\n" + tbl.to_s + "\n"
end
2018-08-24 14:33:36 -05:00
def dump_archs
require_deps unless @framework_loaded
2018-08-24 14:33:36 -05:00
supported_archs = ARCH_ALL.dup
2018-08-23 21:53:49 -05:00
tbl = Rex::Text::Table.new(
'Indent' => 4,
'Header' => "Framework Architectures [--arch <value>]",
'Columns' =>
[
"Name",
])
2018-08-24 14:33:36 -05:00
supported_archs.sort.each do |name|
2018-08-23 21:53:49 -05:00
tbl << [name]
end
"\n" + tbl.to_s + "\n"
end
def dump_encrypt
require_deps unless @framework_loaded
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
require_deps unless @framework_loaded
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)
require_deps unless @framework_loaded
metadata_cache = Msf::Modules::Metadata::Cache.instance
all_payloads = metadata_cache.get_metadata.select { |m| m.type == 'payload' }
platform_filter = platform ? Msf::Module::PlatformList.transform(platform.split(',').map(&:strip).reject(&:empty?)) : nil
arch_filter = arch ? arch.split(',').map(&:strip).reject(&:empty?) : nil
filtered = all_payloads.select do |m|
next false if arch_filter && (Array(m.arch.to_s.split(',').map(&:strip)) & arch_filter).empty?
next false if platform_filter && m.platform_list && (m.platform_list & platform_filter).empty?
true
end
2016-08-10 13:30:09 -05:00
tbl = Rex::Text::Table.new(
2016-02-12 10:49:18 -06:00
'Indent' => 4,
'Header' => "Framework Payloads (#{all_payloads.size} total) [--payload <value>]",
2016-02-12 10:49:18 -06:00
'Columns' =>
[
"Name",
"Description"
])
filtered.sort_by(&:ref_name).each do |m|
tbl << [ m.ref_name, m.description.to_s.split.join(' ') ]
end
2016-02-12 10:49:18 -06:00
"\n" + tbl.to_s + "\n"
end
def dump_encoders(arch = nil)
require_deps unless @framework_loaded
metadata_cache = Msf::Modules::Metadata::Cache.instance
all_encoders = metadata_cache.get_metadata.select { |m| m.type == 'encoder' }
arch_filter = arch ? arch.split(',').map(&:strip).reject(&:empty?) : nil
filtered = all_encoders.select do |m|
next false if arch_filter && (Array(m.arch.to_s.split(',').map(&:strip)) & arch_filter).empty?
true
end
2016-08-10 13:30:09 -05:00
tbl = Rex::Text::Table.new(
2016-02-12 10:49:18 -06:00
'Indent' => 4,
'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : "") + " [--encoder <value>]",
2016-02-12 10:49:18 -06:00
'Columns' =>
[
"Name",
"Rank",
"Description"
])
cnt = 0
filtered.sort_by(&:ref_name).each do |m|
tbl << [ m.ref_name, Msf::RankingName[m.rank] || 'normal', m.name ]
cnt += 1
end
2013-07-01 17:15:48 -05:00
(cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n"
2016-02-12 10:49:18 -06:00
end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
def dump_nops
require_deps unless @framework_loaded
metadata_cache = Msf::Modules::Metadata::Cache.instance
all_nops = metadata_cache.get_metadata.select { |m| m.type == 'nop' }
2016-08-10 13:30:09 -05:00
tbl = Rex::Text::Table.new(
2016-02-12 10:49:18 -06:00
'Indent' => 4,
'Header' => "Framework NOPs (#{all_nops.size} total)",
2016-02-12 10:49:18 -06:00
'Columns' =>
[
"Name",
"Description"
])
all_nops.sort_by(&:ref_name).each do |m|
tbl << [ m.ref_name, m.description.to_s.split.join(' ') ]
end
2016-02-12 10:49:18 -06:00
"\n" + tbl.to_s + "\n"
end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
begin
generator_opts = parse_args(ARGV)
2016-05-09 21:25:23 +02:00
rescue HelpError => e
$stderr.puts e.message
exit(1)
2018-03-17 01:24:54 -05:00
rescue MsfVenomError => e
2016-02-12 10:49:18 -06:00
$stderr.puts "Error: #{e.message}"
exit(1)
end
2013-07-01 17:15:48 -05:00
if generator_opts[:refresh_cache]
warn 'Refreshing module metadata cache...'
require_deps unless @framework_loaded
@framework = ::Msf::Simple::Framework.create({})
# Ensure the background cache load thread has finished before refreshing,
# otherwise it can race and overwrite the refreshed in-memory cache.
Msf::Modules::Metadata::Cache.instance.get_metadata
framework.modules.refresh_cache_from_module_files
warn 'Module cache successfully refreshed.'
# If no other action was requested, exit after refreshing
unless generator_opts[:list] || generator_opts[:list_options] || generator_opts[:payload] != 'stdin'
exit(0)
end
end
2016-02-12 10:49:18 -06:00
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])
2016-02-12 10:49:18 -06:00
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
2018-08-24 14:33:36 -05:00
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"
2016-02-12 10:49:18 -06:00
$stdout.puts dump_payloads
$stdout.puts dump_encoders
$stdout.puts dump_nops
$stdout.puts dump_platforms
2018-08-24 14:33:36 -05:00
$stdout.puts dump_archs
$stdout.puts dump_encrypt
$stdout.puts dump_formats
2016-02-12 10:49:18 -06:00
else
2018-08-24 14:33:36 -05:00
$stderr.puts "Invalid type (#{mod}). These are valid: payloads, encoders, nops, platforms, archs, encrypt, formats, all"
2016-02-12 10:49:18 -06:00
end
2014-02-05 15:25:14 -06:00
end
2016-02-12 10:49:18 -06:00
exit(0)
end
2013-07-01 17:15:48 -05:00
2016-02-12 10:49:18 -06:00
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)
2013-07-01 17:15:48 -05:00
end
2011-05-25 14:41:29 +00:00
$stderr.puts "Options for #{payload_mod.fullname}:\n" + "="*25 + "\n\n"
2016-02-12 10:49:18 -06:00
$stdout.puts ::Msf::Serializer::ReadableText.dump_module(payload_mod, ' ')
$stderr.puts "\nAdvanced options for #{payload_mod.fullname}:\n" + "="*25 + "\n\n"
2016-02-12 10:49:18 -06:00
$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, ' ')
2016-02-12 10:49:18 -06:00
exit(0)
end
2016-02-12 10:49:18 -06:00
generator_opts[:framework] = framework
generator_opts[:cli] = true
2016-02-12 10:49:18 -06:00
begin
venom_generator = Msf::PayloadGenerator.new(generator_opts)
payload = venom_generator.generate_payload
2019-05-08 05:37:46 -04:00
rescue Msf::InvalidFormat => e
$stderr.puts "Error: #{e.message}"
$stderr.puts dump_formats
2016-02-12 10:49:18 -06:00
rescue ::Exception => e
elog("#{e.class} : #{e.message}\n#{e.backtrace * "\n"}")
$stderr.puts "Error: #{e.message}"
end
2014-02-13 10:19:26 -06:00
2016-02-12 10:49:18 -06:00
# No payload generated, no point to go on
exit(2) unless payload
2016-02-12 10:49:18 -06:00
if generator_opts[:out]
2013-07-01 17:15:48 -05:00
begin
2016-02-12 10:49:18 -06:00
::File.open(generator_opts[:out], 'wb') do |f|
f.write(payload)
end
$stderr.puts "Saved as: #{generator_opts[:out]}"
2014-02-05 15:25:14 -06:00
rescue ::Exception => e
2016-02-12 10:49:18 -06:00
# 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"}")
2015-07-03 02:38:52 -05:00
$stderr.puts "Error: #{e.message}"
2013-07-01 17:15:48 -05:00
end
2016-02-12 10:49:18 -06:00
else
output_stream = $stdout
output_stream.binmode
output_stream.write payload
# trailing newline for pretty output
$stderr.puts unless payload =~ /\n$/
2013-11-21 00:57:08 +01:00
end