Skip to content

Commit 9545186

Browse files
committed
More msfvenom refactoring
* Make @framework into a caching method instead * Allow instantiating with streams for where payloads and comments should go. This allows us to capture std{out,err} when running specs * Specs are still woefully under-representative * Get rid of all the calls to exit
1 parent 4b2ae4e commit 9545186

File tree

2 files changed

+185
-67
lines changed

2 files changed

+185
-67
lines changed

msfvenom

Lines changed: 66 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,35 @@ class MsfVenom
2020
class MsfVenomError < StandardError; end
2121
class UsageError < MsfVenomError; end
2222
class NoTemplateError < MsfVenomError; end
23+
class IncompatibleError < MsfVenomError; end
2324

2425
Status = "[*] "
2526
Error = "[-] "
2627

2728
require 'optparse'
2829

30+
def initialize(in_stream=$stdin, out_stream=$stdout, err_stream=$stderr, framework=nil)
31+
@in = in_stream
32+
@out = out_stream
33+
@err = err_stream
34+
@framework = framework
35+
end
36+
2937
def init_framework(create_opts={})
30-
return @framework if @framework
3138
create_opts[:module_types] ||= [
3239
::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP
3340
]
3441
@framework = ::Msf::Simple::Framework.create(create_opts.merge('DisableDatabase' => true))
3542
end
3643

44+
def framework
45+
return @framework if @framework
46+
47+
init_framework
48+
49+
@framework
50+
end
51+
3752
def parse_args(args)
3853
@opts = {}
3954
@datastore = {}
@@ -42,7 +57,7 @@ class MsfVenom
4257
opt.separator('')
4358
opt.separator('Options:')
4459

45-
opt.on('-p', '--payload [payload]', String, 'Payload to use. Specify a \'-\' or stdin to use custom payloads') do |p|
60+
opt.on('-p', '--payload <payload>', String, 'Payload to use. Specify a \'-\' or stdin to use custom payloads') do |p|
4661
if p == '-'
4762
@opts[:payload] = 'stdin'
4863
else
@@ -57,11 +72,11 @@ class MsfVenom
5772
@opts[:list] = l
5873
end
5974

60-
opt.on('-n', '--nopsled [length]', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
75+
opt.on('-n', '--nopsled <length>', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
6176
@opts[:nopsled] = n.to_i
6277
end
6378

64-
opt.on('-f', '--format [format]', String, "Output format (use --help-formats for a list)") do |f|
79+
opt.on('-f', '--format <format>', String, "Output format (use --help-formats for a list)") do |f|
6580
@opts[:format] = f
6681
end
6782

@@ -70,31 +85,31 @@ class MsfVenom
7085
@opts[:encoder] = e
7186
end
7287

73-
opt.on('-a', '--arch [architecture]', String, 'The architecture to use') do |a|
88+
opt.on('-a', '--arch <architecture>', String, 'The architecture to use') do |a|
7489
@opts[:arch] = a
7590
end
7691

77-
opt.on('--platform [platform]', String, 'The platform of the payload') do |l|
92+
opt.on('--platform <platform>', String, 'The platform of the payload') do |l|
7893
@opts[:platform] = l
7994
end
8095

81-
opt.on('-s', '--space [length]', Integer, 'The maximum size of the resulting payload') do |s|
96+
opt.on('-s', '--space <length>', Integer, 'The maximum size of the resulting payload') do |s|
8297
@opts[:space] = s
8398
end
8499

85-
opt.on('-b', '--bad-chars [list] ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
100+
opt.on('-b', '--bad-chars <list>', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
86101
@opts[:badchars] = b
87102
end
88103

89-
opt.on('-i', '--iterations [count] ', Integer, 'The number of times to encode the payload') do |i|
104+
opt.on('-i', '--iterations <count>', Integer, 'The number of times to encode the payload') do |i|
90105
@opts[:iterations] = i
91106
end
92107

93-
opt.on('-c', '--add-code [path] ', String, 'Specify an additional win32 shellcode file to include') do |x|
108+
opt.on('-c', '--add-code <path>', String, 'Specify an additional win32 shellcode file to include') do |x|
94109
@opts[:addshellcode] = x
95110
end
96111

97-
opt.on('-x', '--template [path] ', String, 'Specify a custom executable file to use as a template') do |x|
112+
opt.on('-x', '--template <path>', String, 'Specify a custom executable file to use as a template') do |x|
98113
@opts[:template] = x
99114
end
100115

@@ -120,12 +135,11 @@ class MsfVenom
120135
end
121136

122137
begin
123-
opt.parse!
138+
opt.parse!(args)
124139
rescue OptionParser::InvalidOption => e
125-
p e
126140
raise UsageError, "Invalid option\n#{opt}"
127141
rescue OptionParser::MissingArgument => e
128-
p e
142+
raise UsageError, "Missing required argument for option\n#{opt}"
129143
end
130144

131145
if @opts.empty?
@@ -145,20 +159,20 @@ class MsfVenom
145159
end
146160

147161
def print_status(msg)
148-
$stderr.puts(Status + msg)
162+
@err.puts(Status + msg)
149163
end
150164

151165
def print_error(msg)
152-
$stderr.puts(Error + msg)
166+
@err.puts(Error + msg)
153167
end
154168

155169
def get_encoders(arch, encoder)
156170
encoders = []
157171

158172
if (encoder)
159-
encoders << @framework.encoders.create(encoder)
173+
encoders << framework.encoders.create(encoder)
160174
else
161-
@framework.encoders.each_module_ranked(
175+
framework.encoders.each_module_ranked(
162176
'Arch' => arch ? arch.split(',') : nil) { |name, mod|
163177
encoders << mod.new
164178
}
@@ -168,8 +182,8 @@ class MsfVenom
168182
end
169183

170184
def payload_stdin
171-
$stdin.binmode
172-
payload = $stdin.read
185+
@in.binmode
186+
payload = @in.read
173187
payload
174188
end
175189

@@ -178,14 +192,14 @@ class MsfVenom
178192
nop_jpts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ]
179193

180194
if nop_mod
181-
nop = @framework.nops.create(nop_mod)
195+
nop = framework.nops.create(nop_mod)
182196
raw = nop.generate_sled(len, nop_opts)
183197
return raw if raw
184198
end
185199

186-
@framework.nops.each_module_ranked('Arch' => arch) do |name, mod|
200+
framework.nops.each_module_ranked('Arch' => arch) do |name, mod|
187201
begin
188-
nop = @framework.nops.create(name)
202+
nop = framework.nops.create(name)
189203
raw = nop.generate_sled(len, nop_opts)
190204
return raw if raw
191205
rescue
@@ -198,14 +212,14 @@ class MsfVenom
198212
init_framework(:module_types => [ ::Msf::MODULE_PAYLOAD ])
199213
tbl = Rex::Ui::Text::Table.new(
200214
'Indent' => 4,
201-
'Header' => "Framework Payloads (#{@framework.stats.num_payloads} total)",
215+
'Header' => "Framework Payloads (#{framework.stats.num_payloads} total)",
202216
'Columns' =>
203217
[
204218
"Name",
205219
"Description"
206220
])
207221

208-
@framework.payloads.each_module { |name, mod|
222+
framework.payloads.each_module { |name, mod|
209223
tbl << [ name, mod.new.description ]
210224
}
211225

@@ -225,7 +239,7 @@ class MsfVenom
225239
])
226240
cnt = 0
227241

228-
@framework.encoders.each_module(
242+
framework.encoders.each_module(
229243
'Arch' => arch ? arch.split(',') : nil) { |name, mod|
230244
tbl << [ name, mod.rank_to_s, mod.new.name ]
231245

@@ -239,14 +253,14 @@ class MsfVenom
239253
init_framework(:module_types => [ ::Msf::MODULE_NOP ])
240254
tbl = Rex::Ui::Text::Table.new(
241255
'Indent' => 4,
242-
'Header' => "Framework NOPs (#{@framework.stats.num_nops} total)",
256+
'Header' => "Framework NOPs (#{framework.stats.num_nops} total)",
243257
'Columns' =>
244258
[
245259
"Name",
246260
"Description"
247261
])
248262

249-
@framework.nops.each_module { |name, mod|
263+
framework.nops.each_module { |name, mod|
250264
tbl << [ name, mod.new.description ]
251265
}
252266

@@ -270,15 +284,14 @@ class MsfVenom
270284
@opts[:platform] = ::Msf::Module::PlatformList.transform("Windows")
271285
end
272286
else
273-
payload = @framework.payloads.create(@opts[:payload])
287+
payload = framework.payloads.create(@opts[:payload])
274288
if payload.nil?
275-
print_error("Invalid payload: #{@opts[:payload]}")
276-
exit
289+
raise UsageError, "Invalid payload: #{@opts[:payload]}"
277290
end
278291
if @opts[:list_options]
279292
print_status("Options for #{payload.fullname}\n\n" +
280293
::Msf::Serializer::ReadableText.dump_module(payload,' '))
281-
exit
294+
return
282295
end
283296
@opts[:arch] ||= payload.arch[0]
284297
# If it's not stdin, we'll already have a PlatformList
@@ -299,23 +312,23 @@ class MsfVenom
299312
@opts[:list].each do |mod|
300313
case mod.downcase
301314
when "payloads"
302-
$stderr.puts dump_payloads
315+
@err.puts dump_payloads
303316
when "encoders"
304-
$stderr.puts dump_encoders(@opts[:arch])
317+
@err.puts dump_encoders(@opts[:arch])
305318
when "nops"
306-
$stderr.puts dump_nops
319+
@err.puts dump_nops
307320
when "all"
308321
# Init here so #dump_payloads doesn't create a framework with
309322
# only payloads, etc.
310323
init_framework
311-
$stderr.puts dump_payloads
312-
$stderr.puts dump_encoders
313-
$stderr.puts dump_nops
324+
@err.puts dump_payloads
325+
@err.puts dump_encoders
326+
@err.puts dump_nops
314327
else
315328
print_error("Invalid module type")
316329
end
317330
end
318-
exit
331+
return
319332
end
320333

321334
# Normalize the options
@@ -326,7 +339,6 @@ class MsfVenom
326339
@opts[:encode] ||= !(@opts[:badchars].nil? or @opts[:badchars].empty?)
327340

328341

329-
init_framework
330342
payload_raw = generate_raw_payload
331343

332344
if @opts[:template]
@@ -391,69 +403,66 @@ class MsfVenom
391403
rescue => e
392404
print_error("#{enc.refname} failed: #{e.class} #{e}")
393405
e.backtrace.each { |el|
394-
$stderr.puts(el.to_s)
406+
@err.puts(el.to_s)
395407
}
396408
end
397409
end
398410
end
399411

400412
if @opts[:nopsled]
401-
#puts @opts[:arch].class
402413
nopts = { 'BadChars' => @opts[:badchars] }
403414
nops = generate_nops([@opts[:arch]], @opts[:nopsled], nil, nopts)
404415
payload_raw = nops + payload_raw
405416
end
406417

407-
$stdout.binmode
418+
@out.binmode
408419

409420
case @opts[:format].downcase
410421
# Special-case this to check endianness
411422
when "js_be"
412423

413424
if Rex::Arch.endian(payload.arch) != ENDIAN_BIG
414-
print_error("Big endian format selected for a non big endian payload")
415-
exit
425+
raise IncompatibleError, "Big endian format selected for a non big endian payload"
416426
end
417-
$stdout.write ::Msf::Simple::Buffer.transform(payload_raw, @opts[:format])
427+
@out.write ::Msf::Simple::Buffer.transform(payload_raw, @opts[:format])
418428

419429
# Special-case this so we can build a war directly from the payload if
420430
# possible
421431
when "war"
422-
exe = ::Msf::Util::EXE.to_executable_fmt(@framework, @opts[:arch], @opts[:platform], payload_raw, @opts[:format], exeopts)
432+
exe = ::Msf::Util::EXE.to_executable_fmt(framework, @opts[:arch], @opts[:platform], payload_raw, @opts[:format], exeopts)
423433
if (!exe and payload.platform.platforms.index(::Msf::Module::Platform::Java))
424434
exe = payload.generate_war.pack
425435
else
426436
exe = ::Msf::Util::EXE.to_jsp_war(exe)
427437
end
428-
$stdout.write exe
438+
@out.write exe
429439

430440
# Same as war, special-case this so we can build a jar directly from the
431441
# payload if possible
432442
when "java"
433-
exe = ::Msf::Util::EXE.to_executable_fmt(@framework, @opts[:arch], @opts[:platform], payload_raw, @opts[:format], exeopts)
443+
exe = ::Msf::Util::EXE.to_executable_fmt(framework, @opts[:arch], @opts[:platform], payload_raw, @opts[:format], exeopts)
434444
if(!exe and payload.platform.platforms.index(::Msf::Module::Platform::Java))
435445
exe = payload.generate_jar.pack
436446
end
437447
if exe
438-
$stdout.write exe
448+
@out.write exe
439449
else
440450
print_error("Could not generate payload format")
441451
end
442452

443453
when *::Msf::Simple::Buffer.transform_formats
444-
$stdout.write ::Msf::Simple::Buffer.transform(payload_raw, @opts[:format])
454+
@out.write ::Msf::Simple::Buffer.transform(payload_raw, @opts[:format])
445455

446456
when *::Msf::Util::EXE.to_executable_fmt_formats
447-
exe = ::Msf::Util::EXE.to_executable_fmt(@framework, @opts[:arch], @opts[:platform], payload_raw, @opts[:format], exeopts)
457+
exe = ::Msf::Util::EXE.to_executable_fmt(framework, @opts[:arch], @opts[:platform], payload_raw, @opts[:format], exeopts)
448458
if exe.nil?
449-
print_error("This format does not support that platform/architecture")
450-
exit
459+
raise IncompatibleError, "This format does not support that platform/architecture"
451460
end
452-
$stdout.write exe
461+
@out.write exe
453462

454463
else
455-
print_error("Unsupported format")
456-
exit
464+
raise IncompatibleError, "Unsupported format"
465+
return
457466
end
458467
end
459468
end

0 commit comments

Comments
 (0)