268 lines
7.8 KiB
Ruby
268 lines
7.8 KiB
Ruby
|
|
require 'spec_helper'
|
|
require 'msf/core'
|
|
# doesn't end in .rb or .so, so have to load instead of require
|
|
load File.join(Msf::Config.install_root, 'msfvenom')
|
|
|
|
shared_examples_for "nop dumper" do
|
|
it "should list known nops" do
|
|
%w!
|
|
x86/opty2
|
|
armle/simple
|
|
!.each do |name|
|
|
dump.should include(name)
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for "encoder dumper" do
|
|
it "should list known encoders" do
|
|
%w!
|
|
generic/none
|
|
x86/shikata_ga_nai
|
|
x64/xor
|
|
!.each do |name|
|
|
dump.should include(name)
|
|
end
|
|
end
|
|
end
|
|
|
|
shared_examples_for "payload dumper" do
|
|
it "should list known payloads" do
|
|
# Just a representative sample of some of the important ones.
|
|
%w!
|
|
cmd/unix/reverse
|
|
java/meterpreter/reverse_tcp
|
|
java/meterpreter/reverse_https
|
|
linux/x86/shell/reverse_tcp
|
|
linux/x86/shell_reverse_tcp
|
|
linux/x64/shell/reverse_tcp
|
|
linux/x64/shell_reverse_tcp
|
|
linux/armle/shell/reverse_tcp
|
|
linux/armle/shell_reverse_tcp
|
|
linux/mipsbe/shell_reverse_tcp
|
|
php/meterpreter/reverse_tcp
|
|
windows/meterpreter/reverse_tcp
|
|
windows/meterpreter/reverse_https
|
|
!.each do |name|
|
|
dump.should include(name)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe MsfVenom do
|
|
|
|
let(:stdin) { StringIO.new("", "rb") }
|
|
let(:stdout) { StringIO.new("", "wb") }
|
|
let(:stderr) { StringIO.new("", "wb") }
|
|
subject(:venom) { described_class.new(stdin, stdout, stderr, framework) }
|
|
before(:each) do
|
|
conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config')
|
|
conf_dir.mkpath
|
|
end
|
|
after(:each) do
|
|
dummy_dir = Metasploit::Framework.root.join('spec', 'dummy')
|
|
dummy_dir.rmtree
|
|
end
|
|
|
|
before(:all) do
|
|
conf_dir = Metasploit::Framework.root.join('spec', 'dummy', 'framework','config')
|
|
conf_dir.mkpath
|
|
create_opts = {
|
|
:module_types => [
|
|
::Msf::MODULE_PAYLOAD, ::Msf::MODULE_ENCODER, ::Msf::MODULE_NOP
|
|
],
|
|
'ConfigDirectory' => conf_dir.to_s,
|
|
'DisableDatabase' => true
|
|
}
|
|
@framework = ::Msf::Simple::Framework.create(create_opts)
|
|
end
|
|
|
|
let(:framework) { @framework }
|
|
describe "#dump_encoders" do
|
|
it_behaves_like "encoder dumper" do
|
|
let(:dump) { venom.dump_encoders }
|
|
end
|
|
end
|
|
|
|
describe "#dump_nops" do
|
|
it_behaves_like "nop dumper" do
|
|
let(:dump) { venom.dump_nops }
|
|
end
|
|
end
|
|
|
|
describe "#dump_payloads" do
|
|
it_behaves_like "payload dumper" do
|
|
let(:dump) { venom.dump_payloads }
|
|
end
|
|
end
|
|
|
|
describe "#parse_args" do
|
|
|
|
context "help" do
|
|
it "should raise UsageError" do
|
|
expect { venom.parse_args(%w! -h !) }.to raise_error(MsfVenom::UsageError)
|
|
expect { venom.parse_args(%w! --help !) }.to raise_error(MsfVenom::UsageError)
|
|
expect { venom.parse_args(%w! --help-formats !) }.to raise_error(MsfVenom::UsageError)
|
|
end
|
|
end
|
|
|
|
context "with bad arguments" do
|
|
|
|
it "should raise UsageError with empty arguments" do
|
|
expect { venom.parse_args([]) }.to raise_error(MsfVenom::UsageError)
|
|
end
|
|
|
|
it "should raise with unexpected options" do
|
|
expect { venom.parse_args(%w! --non-existent-option !) }.to raise_error(MsfVenom::UsageError)
|
|
end
|
|
|
|
%w! --platform -a -b -c -f -p -n -s -i -x !.each do |required_arg|
|
|
it "should raise UsageError with no arg for option #{required_arg}" do
|
|
expect { venom.parse_args([required_arg]) }.to raise_error(MsfVenom::UsageError)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
describe "#generate_raw_payload" do
|
|
|
|
before do
|
|
venom.parse_args(args)
|
|
end
|
|
|
|
context "with --options" do
|
|
|
|
context "and a payload" do
|
|
let(:args) { %w! -o -p windows/meterpreter/reverse_tcp ! }
|
|
it "should print options" do
|
|
expect { venom.generate_raw_payload }.to_not raise_error
|
|
output = stderr.string
|
|
output.should include("LHOST")
|
|
output.should include("LPORT")
|
|
end
|
|
context "and some datastore options" do
|
|
it "should print options" do
|
|
venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp LPORT=1234!
|
|
expect { venom.generate_raw_payload }.to_not raise_error
|
|
output = stderr.string
|
|
output.should include("LHOST")
|
|
output.should match(/LPORT\s+1234/)
|
|
end
|
|
|
|
it "should print options case-insensitively" do
|
|
venom.parse_args %w! -o -p windows/meterpreter/reverse_tcp lPoRt=1234!
|
|
expect { venom.generate_raw_payload }.to_not raise_error
|
|
output = stderr.string
|
|
output.should include("LHOST")
|
|
output.should match(/LPORT\s+1234/)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "and an invalid payload" do
|
|
let(:args) { %w! -o -p asdf! }
|
|
it "should raise" do
|
|
expect { venom.generate_raw_payload }.to raise_error(MsfVenom::UsageError)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
[
|
|
{ :format => "elf", :arch => "x86" },
|
|
{ :format => "raw", :arch => "x86" },
|
|
{ :format => "elf", :arch => "armle" },
|
|
{ :format => "raw", :arch => "armle" },
|
|
{ :format => "elf", :arch => "ppc" },
|
|
{ :format => "raw", :arch => "ppc" },
|
|
{ :format => "elf", :arch => "mipsle" },
|
|
{ :format => "raw", :arch => "mipsle" },
|
|
].each do |format_hash|
|
|
format = format_hash[:format]
|
|
arch = format_hash[:arch]
|
|
|
|
context "building #{format} with linux/#{arch}/shell_bind_tcp" do
|
|
let(:args) { %W! -f #{format} -p linux/#{arch}/shell_bind_tcp ! }
|
|
# We're not encoding, so should be testable here
|
|
it "should contain /bin/sh" do
|
|
output = venom.generate_raw_payload
|
|
# Usually push'd in two instructions, so the whole string
|
|
# isn't all together. Check for the two pieces seperately
|
|
output.should include("/sh")
|
|
output.should include("/bin")
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
describe "#generate" do
|
|
include_context 'Msf::Util::Exe'
|
|
|
|
before { venom.parse_args(args) }
|
|
|
|
context "with --list" do
|
|
|
|
context "with invalid module type" do
|
|
let(:args) { %w!--list asdf! }
|
|
it "should raise UsageError" do
|
|
expect { venom.generate }.to raise_error(MsfVenom::UsageError)
|
|
end
|
|
end
|
|
|
|
[ "nop", "encoder", "payload" ].each do |type|
|
|
context "#{type}s" do
|
|
let(:args) { %W!--list #{type}s! }
|
|
it_behaves_like "#{type} dumper" do
|
|
let(:dump) do
|
|
venom.generate
|
|
stderr.string
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
context "with invalid datastore option" do
|
|
let(:args) { %w!-f exe -p windows/shell_reverse_tcp LPORT=asdf! }
|
|
it "should fail validation" do
|
|
expect { venom.generate }.to raise_error(Msf::OptionValidateError)
|
|
end
|
|
end
|
|
|
|
context "without required datastore option" do
|
|
# Requires LHOST
|
|
let(:args) { %w!-f exe -p windows/shell_reverse_tcp! }
|
|
it "should fail validation" do
|
|
expect { venom.generate }.to raise_error(Msf::OptionValidateError)
|
|
end
|
|
end
|
|
|
|
@platform_format_map.each do |plat, formats|
|
|
formats.each do |format_hash|
|
|
format = format_hash[:format]
|
|
arch = format_hash[:arch]
|
|
# Need a new context for each so the let() will work correctly
|
|
context "with format=#{format} platform=#{plat} arch=#{arch}" do
|
|
# This will build executables with no payload. They won't work
|
|
# of course, but at least we can see that it is producing the
|
|
# correct file format for the given arch and platform.
|
|
let(:args) { %W! -p - -f #{format} -a #{arch} --platform #{plat} ! }
|
|
it "should print a #{format} to stdout" do
|
|
venom.generate
|
|
output = stdout.string
|
|
verify_bin_fingerprint(format_hash, output)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|