Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/spec/msf/ui/console/module_argument_parsing_spec.rb
Views: 11655
require 'rspec'12RHOST_EXAMPLES = [3'192.168.172.1',4'192.168.172.1/32',5'file:foo.txt',6'example',7'localhost',8'example.com',9'http://example.com',10'https://example.com:443',11'https://example.com:443/foo/bar?baz=qux&a=b',12'cidr:/30:http://multiple_ips.example.com/foo',13'http://[::ffff:7f00:1]:8000/',14'smb://example.com/',15'smb://[email protected]/',16'smb://user:[email protected]',17'smb://:@example.com',18'smb://domain;user:[email protected]/'19].freeze2021# Shared examples to ensure that all command parsing supports the same ways of22# supplying inline datastore values23RSpec.shared_examples_for 'a command which parses datastore values' do |opts|24context 'when the -o option flag is supplied' do25it 'shows the help menu when no value is supplied' do26expect(subject.send(opts[:method_name], ['-o'])).to be_nil27expect(subject).to have_received(opts[:expected_help_cmd])28end2930it 'allows setting one value' do31expected_result = {32datastore_options: {33'RHOSTS' => '192.168.172.1'34}35}36expect(subject.send(opts[:method_name], ['-o', 'RHOSTS=192.168.172.1'])).to include(expected_result)37end3839it 'allows setting namespaced datastore options' do40expected_result = {41datastore_options: {42'SMB::PROTOCOLVERSION' => '1,2'43}44}45expect(subject.send(opts[:method_name], ['SMB::ProtocolVersion=1,2'])).to include(expected_result)46end4748it 'allows setting datastore options with underscores' do49expected_result = {50datastore_options: {51'USER_FILE' => './example.txt'52}53}54expect(subject.send(opts[:method_name], ['user_file=./example.txt'])).to include(expected_result)55end5657it 'allows setting multiple options individually' do58expected_result = {59datastore_options: {60'RHOSTS' => '192.168.172.1 192.168.172.2',61'RPORT' => '1337'62}63}64expect(subject.send(opts[:method_name], ['-o', 'RHOSTS=192.168.172.1', '-o', 'RPORT=1337', '-o', 'rhosts=192.168.172.2'])).to include(expected_result)65end6667it 'parses the option str directly into its components' do68expected_result = {69datastore_options: {70'RHOSTS' => '192.168.172.1',71'RPORT' => '1337'72}73}74expect(subject.send(opts[:method_name], ['-o', 'RHOSTS=192.168.172.1,RPORT=1337'])).to include(expected_result)75end7677it 'handles arguments containing spaces' do78args = ['-o', 'RHOSTS=http://user:this is a [email protected]']79expected_result = {80datastore_options: {81'RHOSTS' => '"http://user:this is a [email protected]"'82}83}84expect(subject.send(opts[:method_name], args)).to include(expected_result)85end8687RHOST_EXAMPLES.each do |value|88it "parses the option str correctly for rhost #{value.inspect}" do89expected_result = {90datastore_options: {91'RHOSTS' => value,92'RPORT' => '1337'93}94}95expect(subject.send(opts[:method_name], ['-o', "RHOSTS=#{value},RPORT=1337"])).to include(expected_result)96end97end9899it 'correctly handles combinations of inline options, arguments, and option str being provided' do100args = [101'-o', 'RHOSTS=192.168.172.1,RPORT=1337',102'192.168.172.2',103'LPORT=5555'104]105expected_result = {106datastore_options: {107'RHOSTS' => '192.168.172.1 192.168.172.2',108'RPORT' => '1337',109'LPORT' => '5555'110}111}112expect(subject.send(opts[:method_name], args)).to include(expected_result)113end114end115116context 'when arbitrary datastore key value pairs are provided' do117it 'allows setting one value' do118expected_result = {119datastore_options: {120'RHOSTS' => '192.168.172.1'121}122}123expect(subject.send(opts[:method_name], ['RHOSTS=192.168.172.1'])).to include(expected_result)124end125126it 'allows setting multiple options individually' do127expected_result = {128datastore_options: {129'RHOSTS' => '192.168.172.1',130'RPORT' => '1337'131}132}133expect(subject.send(opts[:method_name], ['RHOSTS=192.168.172.1', 'RPORT=1337'])).to include(expected_result)134end135136it 'correctly handles a missing value' do137expected_result = {138datastore_options: {139'RPORT' => ''140}141}142expect(subject.send(opts[:method_name], ['RPORT='])).to include(expected_result)143end144145it 'handles multiple values' do146args = ['RHOSTS=192.168.172.1', 'rhosts=192.168.172.2', 'rhost=smb://user:a b [email protected]']147expected_result = {148datastore_options: {149'RHOSTS' => '192.168.172.1 192.168.172.2 "smb://user:a b [email protected]"'150}151}152expect(subject.send(opts[:method_name], args)).to include(expected_result)153end154155it 'handles whitespaces' do156args = ['rhosts=http://user:this is a [email protected]', 'http://user:[email protected]']157expected_result = {158datastore_options: {159'RHOSTS' => '"http://user:this is a [email protected]" http://user:[email protected]'160}161}162expect(subject.send(opts[:method_name], args)).to include(expected_result)163end164end165166context 'when arguments that resemble an RHOST value are used' do167it 'handles arguments containing spaces' do168args = ['http://user:this is a [email protected]', 'http://user:[email protected]']169expected_result = {170datastore_options: {171'RHOSTS' => '"http://user:this is a [email protected]" http://user:[email protected]'172}173}174expect(subject.send(opts[:method_name], args)).to include(expected_result)175end176177RHOST_EXAMPLES.each do |value|178it "works with a single value of #{value}" do179expected_result = {180datastore_options: {181'RHOSTS' => value182}183}184expect(subject.send(opts[:method_name], [value])).to include(expected_result)185end186187it 'works with multiple values' do188expected_result = {189datastore_options: {190'RHOSTS' => "#{value} #{value} #{value}"191}192}193expect(subject.send(opts[:method_name], [value, value, value])).to include(expected_result)194end195196it 'works with arbitrary option values' do197expected_result = {198datastore_options: {199'RHOSTS' => "#{value} #{value}",200'RPORT' => '2000',201'LPORT' => '5555'202}203}204expect(subject.send(opts[:method_name], ['-o', "RHOSTS=#{value}", '-o', 'RPORT=2000', value, 'LPORT=5555'])).to include(expected_result)205end206end207end208end209210RSpec.shared_examples_for 'a command which shows help menus' do |opts|211it 'shows the help menu with the -h flag' do212expect(subject.send(opts[:method_name], ['-h'])).to be_nil213expect(subject).to have_received(opts[:expected_help_cmd])214end215216it 'shows the help menu with --help flag' do217expect(subject.send(opts[:method_name], ['--help'])).to be_nil218expect(subject).to have_received(opts[:expected_help_cmd])219end220221[222['--foo'],223['--foo', 'bar'],224].each do |args|225it "shows the help menu with unknown flags #{args.inspect}" do226expect(subject.send(opts[:method_name], args)).to be_nil227expect(subject).to have_received(opts[:expected_help_cmd])228end229end230end231232RSpec.describe Msf::Ui::Console::ModuleArgumentParsing do233include_context 'Msf::UIDriver'234235let(:framework) { nil }236let(:subject) do237described_class = self.described_class238dummy_class = Class.new do239include Msf::Ui::Console::ModuleCommandDispatcher240include described_class241242# Method not provided by the mixin, needs to be implemented by class that mixes in described_class243def cmd_run_help244# noop245end246247# Method not provided by the mixin, needs to be implemented by class that mixes in described_class248def cmd_exploit_help249# noop250end251end252instance = dummy_class.new(driver)253instance254end255256before do257allow(subject).to receive(:cmd_run_help)258allow(subject).to receive(:cmd_exploit_help)259allow(subject).to receive(:cmd_check_help)260end261262describe '#parse_check_opts' do263let(:current_mod) { instance_double Msf::Auxiliary, datastore: {} }264265before do266allow(subject).to receive(:mod).and_return(current_mod)267end268269it_behaves_like 'a command which parses datastore values',270method_name: 'parse_check_opts',271expected_help_cmd: 'cmd_check_help'272273it_behaves_like 'a command which shows help menus',274method_name: 'parse_check_opts',275expected_help_cmd: 'cmd_check_help'276end277278describe '#parse_run_opts' do279let(:current_mod) { instance_double Msf::Auxiliary, datastore: {} }280281before do282allow(subject).to receive(:mod).and_return(current_mod)283end284285it_behaves_like 'a command which parses datastore values',286method_name: 'parse_run_opts',287expected_help_cmd: 'cmd_run_help'288289it_behaves_like 'a command which shows help menus',290method_name: 'parse_run_opts',291expected_help_cmd: 'cmd_run_help'292293it 'handles an action being supplied' do294args = []295expected_result = {296jobify: false,297quiet: false,298action: 'action-name',299datastore_options: {}300}301expect(subject.parse_run_opts(args, action: 'action-name')).to eq(expected_result)302end303304it 'handles an action being specified from the original datastore value' do305current_mod.datastore['action'] = 'datastore-action-name'306args = []307expected_result = {308jobify: false,309quiet: false,310action: 'action-name',311datastore_options: {}312}313expect(subject.parse_run_opts(args, action: 'action-name')).to eq(expected_result)314end315316it 'handles an action being nil' do317args = []318expected_result = {319jobify: false,320quiet: false,321action: nil,322datastore_options: {}323}324expect(subject.parse_run_opts(args)).to eq(expected_result)325end326end327328describe '#parse_exploit_opts' do329let(:current_mod) { instance_double Msf::Exploit, datastore: {} }330331before do332allow(subject).to receive(:mod).and_return(current_mod)333end334335it_behaves_like 'a command which parses datastore values',336method_name: 'parse_exploit_opts',337expected_help_cmd: 'cmd_exploit_help'338339it_behaves_like 'a command which shows help menus',340method_name: 'parse_exploit_opts',341expected_help_cmd: 'cmd_exploit_help'342343it 'handles no arguments being supplied' do344args = []345expected_result = {346jobify: false,347quiet: false,348datastore_options: {}349}350expect(subject.parse_exploit_opts(args)).to eq(expected_result)351end352353it 'allows multiple exploit options to be set' do354args = [355# encoder356'-e', 'encoder_value',357# force358'-f',359# quiet360'-q',361# nop362'-n', 'nop_value',363# option str364'-o', 'RPORT=9001',365# payload366'-p', 'payload_value',367# target368'-t', '5',369# run in the background370'-z',371# inline option372'LPORT=5555',373# rhosts374'192.168.172.1',375'192.168.172.2',376'example.com'377]378expected_result = {379jobify: false,380quiet: true,381datastore_options: {382'RHOSTS' => '192.168.172.1 192.168.172.2 example.com',383'RPORT' => '9001',384'LPORT' => '5555'385},386encoder: 'encoder_value',387force: true,388nop: 'nop_value',389payload: 'payload_value',390target: 5,391background: true392}393expect(subject.parse_exploit_opts(args)).to eq(expected_result)394end395end396end397398399