metasploit-framework/spec/msfcli_spec.rb

813 lines
19 KiB
Ruby

require 'spec_helper'
load Metasploit::Framework.root.join('msfcli').to_path
require 'msfenv'
require 'msf/ui'
require 'msf/base'
describe Msfcli, :content do
subject(:msfcli) {
described_class.new(args)
}
#
# methods
#
# Get stdout:
# http://stackoverflow.com/questions/11349270/test-output-to-command-line-with-rspec
def get_stdout(&block)
out = $stdout
$stdout = fake = StringIO.new
begin
yield
ensure
$stdout = out
end
fake.string
end
#
# lets
#
let(:args) {
[]
}
context "#initialize" do
context 'with module name' do
let(:args) {
[
module_name,
*params
]
}
let(:module_name) {
'multi/handler'
}
let(:params) {
%w{payload=windows/meterpreter/reverse_tcp lhost=127.0.0.1}
}
let(:parsed_args) {
msfcli.instance_variable_get(:@args)
}
context 'multi/handler' do
context 'with mode' do
let(:args) {
super() + [mode]
}
context 'E' do
let(:mode) {
'E'
}
it 'parses module name into :module_name arg' do
expect(parsed_args[:module_name]).to eq(module_name)
end
it 'parses mode into :mode arg' do
expect(parsed_args[:mode]).to eq(mode)
end
it 'parses module parameters between module name and mode' do
expect(parsed_args[:params]).to eq(params)
end
end
context 's' do
let(:mode) {
's'
}
it "parses mode as 's' (summary)" do
expect(parsed_args[:mode]).to eq(mode)
end
end
end
context 'without mode' do
let(:args) {
[
module_name
]
}
it "parses mode as 'h' (help) by default" do
expect(parsed_args[:mode]).to eq('h')
end
end
end
context 'exploit/windows/browser/ie_cbutton_uaf' do
let(:module_name) {
'exploit/windows/browser/ie_cbutton_uaf'
}
it "strips 'exploit/' prefix for :module_name" do
expect(parsed_args[:module_name]).to eq('windows/browser/ie_cbutton_uaf')
end
end
context 'exploit/windows/browser/ie_cbutton_uaf' do
let(:module_name) {
'exploits/windows/browser/ie_cbutton_uaf'
}
it "strips 'exploits/' prefix for :module_name" do
expect(parsed_args[:module_name]).to eq('windows/browser/ie_cbutton_uaf')
end
end
end
end
context "#usage" do
it "prints Usage" do
out = get_stdout {
msfcli.usage
}
expect(out).to include('Usage')
end
end
#
# This one is slow because we're loading all modules
#
context "#dump_module_list" do
include_context 'Metasploit::Framework::Spec::Constants cleaner'
let(:framework) {
msfcli.framework
}
it 'dumps a listof modules' do
tbl = ''
stdout = get_stdout {
tbl = msfcli.dump_module_list
}
expect(tbl).to include 'Exploits'
expect(stdout).to include 'Please wait'
end
end
context "#guess_payload_name" do
subject(:guess_payload_name) {
msfcli.guess_payload_name(payload_reference_name)
}
context 'with windows/meterpreter/reverse_tcp' do
let(:payload_reference_name) {
'windows/meterpreter/reverse_tcp'
}
it {
is_expected.to eq(
[
/stages\/windows\/meterpreter/,
/payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/
]
)
}
end
context 'with windows/shell/reverse_tcp' do
let(:payload_reference_name) {
'windows/shell/reverse_tcp'
}
it {
is_expected.to eq(
[
/stages\/windows\/shell/,
/payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/
]
)
}
end
context 'with php/meterpreter_reverse_tcp' do
let(:payload_reference_name) {
'php/meterpreter_reverse_tcp'
}
it {
is_expected.to eq(
[
/stages\/php\/meterpreter/,
/payloads\/(stagers|stages)\/php\/.*(meterpreter_reverse_tcp)\.rb$/
]
)
}
end
context 'with linux/x86/meterpreter/reverse_tcp' do
let(:payload_reference_name) {
'linux/x86/meterpreter/reverse_tcp'
}
it {
is_expected.to eq(
[
/stages\/linux\/x86\/meterpreter/,
/payloads\/(stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/
]
)
}
end
context 'with java/meterpreter/reverse_tcp' do
let(:payload_reference_name) {
'java/meterpreter/reverse_tcp'
}
it {
is_expected.to eq(
[
/stages\/java\/meterpreter/,
/payloads\/(stagers|stages)\/java\/.*(reverse_tcp)\.rb$/
]
)
}
end
context 'with cmd/unix/reverse' do
let(:payload_reference_name) {
'cmd/unix/reverse'
}
it {
is_expected.to eq(
[
/stages\/cmd\/shell/,
/payloads\/(singles|stagers|stages)\/cmd\/.*(reverse)\.rb$/
]
)
}
end
context 'with bsd/x86/shell_reverse_tcp' do
let(:payload_reference_name) {
'bsd/x86/shell_reverse_tcp'
}
it {
is_expected.to eq(
[
/stages\/bsd\/x86\/shell/,
/payloads\/(singles|stagers|stages)\/bsd\/x86\/.*(shell_reverse_tcp)\.rb$/
]
)
}
end
end
context "#guess_encoder_name" do
subject(:guess_encoder_name) {
msfcli.guess_encoder_name(encoder_reference_name)
}
context 'with x86/shikata_ga_nai' do
let(:encoder_reference_name) {
'x86/shikata_ga_nai'
}
it {
is_expected.to eq(
[/encoders\/#{encoder_reference_name}/]
)
}
end
end
context "#guess_nop_name" do
subject(:guess_nop_name) {
msfcli.guess_nop_name(nop_reference_name)
}
context 'with x86/shikata_ga_nai' do
let(:nop_reference_name) {
'x86/single_byte'
}
it {
is_expected.to eq(
[/nops\/#{nop_reference_name}/]
)
}
end
end
context "#generate_whitelist" do
subject(:generate_whitelist) {
msfcli.generate_whitelist.map(&:to_s)
}
let(:args) {
[
'multi/handler',
"payload=#{payload_reference_name}",
'lhost=127.0.0.1',
mode
]
}
let(:mode) {
'E'
}
context 'with payload' do
context 'linux/x86/reverse_tcp' do
let(:payload_reference_name) {
'linux/x86/reverse_tcp'
}
context 'with encoder' do
let(:args) {
super().tap { |args|
args.insert(-2, "encoder=#{encoder_reference_name}")
}
}
context 'x86/fnstenv_mov' do
let(:encoder_reference_name) {
'x86/fnstenv_mov'
}
it {
is_expected.to match_array(
[
/multi\/handler/,
/stages\/linux\/x86\/shell/,
/payloads\/(singles|stagers|stages)\/linux\/x86\/.*(reverse_tcp)\.rb$/,
/encoders\/x86\/fnstenv_mov/,
/post\/.+/,
/encoders\/generic\/*/,
/nops\/.+/
].map(&:to_s)
)
}
end
end
end
context 'windows/meterpreter/reverse_tcp' do
let(:payload_reference_name) {
'windows/meterpreter/reverse_tcp'
}
context 'with default options' do
it {
is_expected.to match_array(
[
/multi\/handler/,
/stages\/windows\/meterpreter/,
/payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/,
/post\/.+/,
/encoders\/generic\/*/,
/encoders\/.+/,
/nops\/.+/
].map(&:to_s)
)
}
end
context 'with encoder' do
let(:args) {
super().tap { |args|
args.insert(-2, "encoder=#{encoder_reference_name}")
}
}
context "''" do
let(:encoder_reference_name) do
"''"
end
context 'with post' do
let(:args) {
super().tap { |args|
args.insert(-2, "post=#{post_reference_name}")
}
}
context "''" do
let(:post_reference_name) do
"''"
end
context "with nop" do
let(:args) {
super().tap { |args|
args.insert(-2, "nop=#{nop_reference_name}")
}
}
context "''" do
let(:nop_reference_name) {
"''"
}
it {
is_expected.to match_array(
[
/multi\/handler/,
/stages\/windows\/meterpreter/,
/payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/,
/encoders\/''/,
/post\/''/,
/nops\/''/,
/encoders\/generic\/*/
].map(&:to_s)
)
}
end
end
end
end
end
context "<blank>" do
let(:encoder_reference_name) do
""
end
context 'with post' do
let(:args) {
super().tap { |args|
args.insert(-2, "post=#{post_reference_name}")
}
}
context "<blank>" do
let(:post_reference_name) do
""
end
context "with nop" do
let(:args) {
super().tap { |args|
args.insert(-2, "nop=#{nop_reference_name}")
}
}
context "<blank>" do
let(:nop_reference_name) {
""
}
it {
is_expected.to match_array(
[
/multi\/handler/,
/stages\/windows\/meterpreter/,
/payloads\/(stagers|stages)\/windows\/.*(reverse_tcp)\.rb$/,
/encoders\/generic\/*/
].map(&:to_s)
)
}
end
end
end
end
end
end
end
end
end
context "#init_modules" do
include_context 'Metasploit::Framework::Spec::Constants cleaner'
let(:args) {
[
module_name,
mode
]
}
let(:framework) {
msfcli.framework
}
let(:mode) {
'S'
}
context 'with exploit/windows/smb/psexec' do
let(:module_name) {
'exploit/windows/smb/psexec'
}
it 'creates the module in :module' do
modules = {}
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules[:module]).to be_an Msf::Exploit
expect(modules[:module].fullname).to eq(module_name)
end
end
context 'with auxiliary/server/browser_autopwn' do
let(:module_name) {
'auxiliary/server/browser_autopwn'
}
it 'creates the module in :module' do
modules = {}
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules[:module]).to be_an Msf::Auxiliary
expect(modules[:module].fullname).to eq(module_name)
end
end
context 'with post/windows/gather/credentials/gpp' do
let(:module_name) {
'post/windows/gather/credentials/gpp'
}
it 'creates the module in :module' do
modules = {}
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules[:module]).to be_an Msf::Post
expect(modules[:module].fullname).to eq(module_name)
end
end
context 'with multi/handler' do
let(:module_name) {
'multi/handler'
}
it 'creates the module in :module' do
modules = {}
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules[:module]).to be_an Msf::Exploit
expect(modules[:module].refname).to eq(module_name)
end
context 'with payload' do
let(:args) {
super().tap { |args|
args.insert(-2, "payload=#{payload_reference_name}")
}
}
context 'windows/meterpreter/reverse_tcp' do
let(:payload_reference_name) do
'windows/meterpreter/reverse_tcp'
end
it 'creates payload in :payload' do
modules = {}
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules[:payload]).to be_an Msf::Payload
expect(modules[:payload].refname).to eq(payload_reference_name)
end
end
end
context 'with data store options' do
let(:args) {
super().tap { |args|
args.insert(-2, "#{data_store_key}=#{data_store_value}")
}
}
let(:data_store_key) {
'lhost'
}
let(:data_store_value) {
'127.0.0.1'
}
it 'sets data store on :module' do
modules = {}
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules[:module].datastore[data_store_key]).to eq(data_store_value)
end
end
end
context 'with invalid module name' do
let(:module_name) {
'invalid/module/name'
}
it 'returns empty modules Hash' do
modules = nil
Kernel.quietly {
modules = msfcli.init_modules
}
expect(modules).to eq({})
end
end
end
context "#engage_mode" do
include_context 'Metasploit::Framework::Spec::Constants cleaner'
subject(:engage_mode) {
msfcli.engage_mode(modules)
}
let(:args) {
[
module_name,
mode
]
}
let(:framework) {
msfcli.framework
}
let(:modules) {
msfcli.init_modules
}
context 'with auxiliary/scanner/http/http_put' do
let(:module_name) {
'auxiliary/scanner/http/http_put'
}
context 'with mode' do
context 'ac' do
let(:mode) {
'ac'
}
specify {
expect(get_stdout { engage_mode }).to match(/DELETE/)
}
end
end
end
context 'with auxiliary/scanner/http/http_version' do
let(:module_name) {
'auxiliary/scanner/http/http_version'
}
context 'with mode' do
context 'A' do
let(:mode) {
'A'
}
specify {
expect(get_stdout { engage_mode }).to match(/UserAgent/)
}
end
context 'I' do
let(:mode) {
'I'
}
specify {
expect(get_stdout { engage_mode }).to match(/Insert fake relative directories into the uri/)
}
end
context 'O' do
let(:mode) {
'O'
}
specify {
expect(get_stdout { engage_mode }).to match(/The target address range or CIDR identifier/)
}
end
context 'P' do
let(:mode) {
'P'
}
specify {
expect(get_stdout { engage_mode }).to match(/This type of module does not support payloads/)
}
end
context 's' do
let(:mode) {
's'
}
specify {
expect(get_stdout { engage_mode }).to match %r{Module: auxiliary/scanner/http/http_version}
}
end
context 't' do
let(:mode) {
't'
}
specify {
expect(get_stdout { engage_mode }).to match(/This type of module does not support targets/)
}
end
end
end
context 'with windows/browser/ie_cbutton_uaf' do
let(:module_name) {
'windows/browser/ie_cbutton_uaf'
}
context 'with mode' do
context 'ac' do
let(:mode) {
'ac'
}
specify {
expect(get_stdout { engage_mode }).to match(/This type of module does not support actions/)
}
end
context 'P' do
let(:mode) {
'P'
}
specify {
expect(get_stdout { engage_mode }).to match(/windows\/meterpreter\/reverse_tcp/)
}
end
context 'T' do
let(:mode) {
'T'
}
specify {
expect(get_stdout { engage_mode }).to match(/IE 8 on Windows 7/)
}
end
end
end
context 'with windows/smb/ms08_067_netapi' do
let(:args) {
super().tap { |args|
args.insert(-2, "RHOST=127.0.0.1")
}
}
let(:module_name) {
'windows/smb/ms08_067_netapi'
}
context 'with mode C' do
let(:mode) {
'C'
}
specify {
expect(get_stdout { engage_mode }).to match(/#{Msf::Exploit::CheckCode::Unknown[1]}/)
}
end
end
end
end