Skip to content

Commit 17be444

Browse files
committed
msfvenom: Use metadata cache for --list, add --refresh-cache
1 parent aee4762 commit 17be444

File tree

1 file changed

+68
-34
lines changed

1 file changed

+68
-34
lines changed

msfvenom

100755100644
Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def require_deps
3535
require 'msf/core/payload_generator'
3636
require 'msf/core/constants'
3737

38+
unless $stdout.tty?
39+
Rex::Text::Table.unwrap_tables!
40+
end
41+
3842
@framework_loaded = true
3943
end
4044

@@ -55,10 +59,6 @@ def init_framework(create_opts={})
5559
end
5660

5761
@framework = ::Msf::Simple::Framework.create(create_opts)
58-
59-
unless $stdout.tty?
60-
Rex::Text::Table.unwrap_tables!
61-
end
6262
end
6363

6464
# Cached framework object
@@ -196,6 +196,10 @@ def parse_args(args)
196196
opts[:timeout] = x
197197
end
198198

199+
opt.on('--refresh-cache', 'Rebuild the module metadata cache from disk before listing') do
200+
opts[:refresh_cache] = true
201+
end
202+
199203
opt.on_tail('-h', '--help', 'Show this message') do
200204
raise HelpError, "#{opt}"
201205
end
@@ -227,7 +231,7 @@ def parse_args(args)
227231
opts[:payload] = "stdin"
228232
end
229233

230-
if opts[:payload].downcase == 'stdin' && !opts[:list]
234+
if opts[:payload].downcase == 'stdin' && !opts[:list] && !opts[:refresh_cache]
231235
$stderr.puts "Attempting to read payload from STDIN..."
232236
begin
233237
opts[:timeout] ||= 30
@@ -255,7 +259,7 @@ def payload_stdin
255259
end
256260

257261
def dump_platforms
258-
init_framework(:module_types => [])
262+
require_deps unless @framework_loaded
259263
supported_platforms = []
260264
Msf::Module::Platform.subclasses.each {|c| supported_platforms << c.realname.downcase}
261265

@@ -275,7 +279,7 @@ def dump_platforms
275279
end
276280

277281
def dump_archs
278-
init_framework(:module_types => [])
282+
require_deps unless @framework_loaded
279283
supported_archs = ARCH_ALL.dup
280284

281285
tbl = Rex::Text::Table.new(
@@ -294,7 +298,7 @@ def dump_archs
294298
end
295299

296300
def dump_encrypt
297-
init_framework(:module_types => [])
301+
require_deps unless @framework_loaded
298302
tbl = Rex::Text::Table.new(
299303
'Indent' => 4,
300304
'Header' => "Framework Encryption Formats [--encrypt <value>]",
@@ -311,7 +315,7 @@ def dump_encrypt
311315
end
312316

313317
def dump_formats
314-
init_framework(:module_types => [])
318+
require_deps unless @framework_loaded
315319
tbl1 = Rex::Text::Table.new(
316320
'Indent' => 4,
317321
'Header' => "Framework Executable Formats [--format <value>]",
@@ -340,33 +344,50 @@ def dump_formats
340344
end
341345

342346
def dump_payloads(platform = nil, arch = nil)
343-
init_framework(:module_types => [ :payload ])
347+
require_deps unless @framework_loaded
348+
metadata_cache = Msf::Modules::Metadata::Cache.instance
349+
all_payloads = metadata_cache.get_metadata.select { |m| m.type == 'payload' }
350+
351+
platform_filter = platform ? Msf::Module::PlatformList.transform(platform.split(',').map(&:strip).reject(&:empty?)) : nil
352+
arch_filter = arch ? arch.split(',').map(&:strip).reject(&:empty?) : nil
353+
354+
filtered = all_payloads.select do |m|
355+
next false if arch_filter && (Array(m.arch.to_s.split(',').map(&:strip)) & arch_filter).empty?
356+
357+
next false if platform_filter && m.platform_list && (m.platform_list & platform_filter).empty?
358+
359+
true
360+
end
361+
344362
tbl = Rex::Text::Table.new(
345363
'Indent' => 4,
346-
'Header' => "Framework Payloads (#{framework.stats.num_payloads} total) [--payload <value>]",
364+
'Header' => "Framework Payloads (#{all_payloads.size} total) [--payload <value>]",
347365
'Columns' =>
348366
[
349367
"Name",
350368
"Description"
351369
])
352370

353-
framework.payloads.each_module(
354-
'Platform' => platform ? Msf::Module::PlatformList.transform(platform.split(',')) : nil,
355-
'Arch' => arch ? arch.split(',') : nil) do |name, mod|
356-
begin
357-
mod_info = mod.new.description.split.join(' ')
358-
rescue ::Exception, ::LoadError => e
359-
wlog("Module #{name} failed to initialize: #{e}", 'core', LEV_0)
360-
next
361-
end
362-
tbl << [ name, mod_info ]
371+
filtered.sort_by(&:ref_name).each do |m|
372+
tbl << [ m.ref_name, m.description.to_s.split.join(' ') ]
363373
end
364374

365375
"\n" + tbl.to_s + "\n"
366376
end
367377

368378
def dump_encoders(arch = nil)
369-
init_framework(:module_types => [ :encoder ])
379+
require_deps unless @framework_loaded
380+
metadata_cache = Msf::Modules::Metadata::Cache.instance
381+
all_encoders = metadata_cache.get_metadata.select { |m| m.type == 'encoder' }
382+
383+
arch_filter = arch ? arch.split(',').map(&:strip).reject(&:empty?) : nil
384+
385+
filtered = all_encoders.select do |m|
386+
next false if arch_filter && (Array(m.arch.to_s.split(',').map(&:strip)) & arch_filter).empty?
387+
388+
true
389+
end
390+
370391
tbl = Rex::Text::Table.new(
371392
'Indent' => 4,
372393
'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : "") + " [--encoder <value>]",
@@ -378,29 +399,30 @@ def dump_encoders(arch = nil)
378399
])
379400
cnt = 0
380401

381-
framework.encoders.each_module(
382-
'Arch' => arch ? arch.split(',') : nil) do |name, mod|
383-
tbl << [ name, mod.rank_to_s, mod.new.name ]
384-
385-
cnt += 1
402+
filtered.sort_by(&:ref_name).each do |m|
403+
tbl << [ m.ref_name, Msf::RankingName[m.rank] || 'normal', m.name ]
404+
cnt += 1
386405
end
387406

388407
(cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n"
389408
end
390409

391410
def dump_nops
392-
init_framework(:module_types => [ :nop ])
411+
require_deps unless @framework_loaded
412+
metadata_cache = Msf::Modules::Metadata::Cache.instance
413+
all_nops = metadata_cache.get_metadata.select { |m| m.type == 'nop' }
414+
393415
tbl = Rex::Text::Table.new(
394416
'Indent' => 4,
395-
'Header' => "Framework NOPs (#{framework.stats.num_nops} total)",
417+
'Header' => "Framework NOPs (#{all_nops.size} total)",
396418
'Columns' =>
397419
[
398420
"Name",
399421
"Description"
400422
])
401423

402-
framework.nops.each_module do |name, mod|
403-
tbl << [ name, mod.new.description.split.join(' ') ]
424+
all_nops.sort_by(&:ref_name).each do |m|
425+
tbl << [ m.ref_name, m.description.to_s.split.join(' ') ]
404426
end
405427

406428
"\n" + tbl.to_s + "\n"
@@ -417,6 +439,21 @@ rescue MsfVenomError => e
417439
exit(1)
418440
end
419441

442+
if generator_opts[:refresh_cache]
443+
warn 'Refreshing module metadata cache...'
444+
require_deps unless @framework_loaded
445+
@framework = ::Msf::Simple::Framework.create({})
446+
# Ensure the background cache load thread has finished before refreshing,
447+
# otherwise it can race and overwrite the refreshed in-memory cache.
448+
Msf::Modules::Metadata::Cache.instance.get_metadata
449+
framework.modules.refresh_cache_from_module_files
450+
warn 'Module cache successfully refreshed.'
451+
# If no other action was requested, exit after refreshing
452+
unless generator_opts[:list] || generator_opts[:list_options] || generator_opts[:payload] != 'stdin'
453+
exit(0)
454+
end
455+
end
456+
420457
if generator_opts[:list]
421458
generator_opts[:list].each do |mod|
422459
case mod.downcase
@@ -435,9 +472,6 @@ if generator_opts[:list]
435472
when "formats", "format", "f"
436473
$stdout.puts dump_formats
437474
when "all", "a"
438-
# Init here so #dump_payloads doesn't create a framework with
439-
# only payloads, etc.
440-
init_framework
441475
$stdout.puts dump_payloads
442476
$stdout.puts dump_encoders
443477
$stdout.puts dump_nops

0 commit comments

Comments
 (0)