Path: blob/master/spec/lib/msf/base/serializer/readable_text_spec.rb
32005 views
# -*- coding:binary -*-12require 'spec_helper'3require 'rex/text'45RSpec.describe Msf::Serializer::ReadableText do6# The described_class API takes a mix of strings and whitespace character counts7let(:indent_string) { '' }8let(:indent_length) { indent_string.length }910let(:default_module_options) do11[12Msf::Opt::RHOSTS,13Msf::Opt::RPORT(3000),14Msf::OptString.new(15'foo',16[true, 'Foo option', 'bar']17),18Msf::OptString.new(19'fizz',20[true, 'fizz option', 'buzz']21),22Msf::OptString.new(23'baz',24[true, 'baz option', 'qux']25),26Msf::OptString.new(27'OptionWithModuleDefault',28[true, 'option with module default', true]29),30Msf::OptFloat.new('FloatValue', [false, 'A FloatValue ', 3.5]),31Msf::OptString.new(32'NewOptionName',33[true, 'An option with a new name. Aliases ensure the old and new names are synchronized', 'default_value'],34aliases: ['OLD_OPTION_NAME']35),36Msf::OptString.new(37'SMBUser',38[true, 'The SMB username'],39fallbacks: ['username']40),41Msf::OptString.new(42'SMBDomain',43[true, 'The SMB username', 'WORKGROUP'],44aliases: ['WindowsDomain'],45fallbacks: ['domain']46)47]48end4950let(:default_advanced_module_options) do51[52Msf::OptEnum.new('DigestAlgorithm', [ true, 'The digest algorithm to use', 'SHA256', %w[SHA1 SHA256] ])53]54end5556let(:default_evasion_module_options) do57[58Msf::OptInt.new('EVASION_TEST_OPTION', [ true, 'The evasion test option'])59]60end6162let(:module_options) { default_module_options }63let(:advanced_module_options) { default_advanced_module_options }64let(:evasion_module_options) { default_evasion_module_options }6566# (see Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options#kerberos_auth_options)67def kerberos_auth_options(protocol:, auth_methods:)68mixin = Class.new.extend(Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options)69mixin.kerberos_auth_options(protocol: protocol, auth_methods: auth_methods)70end7172let(:aux_mod) do73mod_klass = Class.new(Msf::Auxiliary) do74def initialize75super(76'Name' => 'mock module',77'Description' => 'mock module',78'Author' => ['Unknown'],79'License' => MSF_LICENSE,80'DefaultOptions' => {81'OptionWithModuleDefault' => false,82'foo' => 'foo_from_module',83'baz' => 'baz_from_module'84},85)86end87end8889mod = mod_klass.new90mod.send(:register_options, module_options)91mod.send(:register_advanced_options, advanced_module_options)92mod.send(:register_evasion_options, evasion_module_options)93mock_framework = instance_double(::Msf::Framework, datastore: Msf::DataStore.new)94allow(mod).to receive(:framework).and_return(mock_framework)95mod96end9798let(:aux_mod_with_set_options) do99mod = aux_mod.replicant100mod.framework.datastore['RHOSTS'] = '192.0.2.2'101mod.framework.datastore['FloatValue'] = 5102mod.framework.datastore['foo'] = 'foo_from_framework'103mod.datastore['foo'] = 'new_value'104mod.datastore.unset('foo')105mod.datastore['OLD_OPTION_NAME'] = nil106mod.datastore['username'] = 'username'107mod.datastore['fizz'] = 'new_fizz'108mod109end110111before(:each) do112allow(Rex::Text::Table).to receive(:wrapped_tables?).and_return(true)113end114115describe '.dump_datastore' do116context 'when the datastore is empty' do117it 'returns the datastore as a table' do118expect(described_class.dump_datastore('Table name', Msf::DataStore.new, indent_length)).to match_table <<~TABLE119Table name120==========121122No entries in data store.123TABLE124end125end126127context 'when the datastore has values' do128it 'returns the datastore as a table' do129expect(described_class.dump_datastore('Table name', aux_mod_with_set_options.datastore, indent_length)).to match_table <<~TABLE130Table name131==========132133Name Value134---- -----135DigestAlgorithm SHA256136EVASION_TEST_OPTION137FloatValue 5138NewOptionName139OptionWithModuleDefault false140RHOSTS 192.0.2.2141RPORT 3000142SMBDomain WORKGROUP143SMBUser username144VERBOSE false145WORKSPACE146baz baz_from_module147fizz new_fizz148foo foo_from_framework149username username150TABLE151end152end153end154155describe '.dump_options' do156context 'when missing is false' do157it 'returns the options as a table' do158expect(described_class.dump_options(aux_mod_with_set_options, indent_string, false)).to match_table <<~TABLE159Name Current Setting Required Description160---- --------------- -------- -----------161FloatValue 5 no A FloatValue162NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized163OptionWithModuleDefault false yes option with module default164RHOSTS 192.0.2.2 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html165RPORT 3000 yes The target port166SMBDomain WORKGROUP yes The SMB username167SMBUser username yes The SMB username168baz baz_from_module yes baz option169fizz new_fizz yes fizz option170foo foo_from_framework yes Foo option171TABLE172end173end174175context 'when missing is true' do176it 'returns the options as a table' do177expect(described_class.dump_options(aux_mod_with_set_options, indent_string, true)).to match_table <<~TABLE178Name Current Setting Required Description179---- --------------- -------- -----------180NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized181TABLE182end183end184185context 'when some options are grouped' do186let(:group_name) { 'group_name' }187let(:group_description) { 'Used for example reasons' }188let(:option_names) { %w[DigestAlgorithm RHOSTS SMBUser SMBDomain] }189let(:group) { Msf::OptionGroup.new(name: group_name, description: group_description, option_names: option_names) }190let(:aux_mod_with_grouped_options) do191mod = aux_mod_with_set_options.replicant192mod.options.add_group(group)193mod194end195196it 'should return the grouped options separate to the rest of the options' do197expect(described_class.dump_options(aux_mod_with_grouped_options, indent_string, false)).to match_table <<~TABLE198Name Current Setting Required Description199---- --------------- -------- -----------200FloatValue 5 no A FloatValue201NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized202OptionWithModuleDefault false yes option with module default203RPORT 3000 yes The target port204baz baz_from_module yes baz option205fizz new_fizz yes fizz option206foo foo_from_framework yes Foo option207208209#{group_description}:210211Name Current Setting Required Description212---- --------------- -------- -----------213RHOSTS 192.0.2.2 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html214SMBDomain WORKGROUP yes The SMB username215SMBUser username yes The SMB username216TABLE217end218end219220context 'when there are multiple options groups' do221let(:group_name_1) { 'group_name_1' }222let(:group_description_1) { 'Used for example reasons_1' }223let(:option_names_1) {['RHOSTS']}224let(:group_name_2) { 'group_name_2' }225let(:group_description_2) { 'Used for example reasons_2' }226let(:option_names_2) { %w[SMBUser SMBDomain] }227228let(:group_1) { Msf::OptionGroup.new(name: group_name_1, description: group_description_1, option_names: option_names_1) }229let(:group_2) { Msf::OptionGroup.new(name: group_name_2, description: group_description_2, option_names: option_names_2) }230231let(:aux_mod_with_grouped_options) do232mod = aux_mod_with_set_options.replicant233mod.options.add_group(group_1)234mod.options.add_group(group_2)235mod236end237238it 'should return the grouped options separate to the rest of the options' do239expect(described_class.dump_options(aux_mod_with_grouped_options, indent_string, false)).to match_table <<~TABLE240Name Current Setting Required Description241---- --------------- -------- -----------242FloatValue 5 no A FloatValue243NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized244OptionWithModuleDefault false yes option with module default245RPORT 3000 yes The target port246baz baz_from_module yes baz option247fizz new_fizz yes fizz option248foo foo_from_framework yes Foo option249250251#{group_description_1}:252253Name Current Setting Required Description254---- --------------- -------- -----------255RHOSTS 192.0.2.2 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html256257258#{group_description_2}:259260Name Current Setting Required Description261---- --------------- -------- -----------262SMBDomain WORKGROUP yes The SMB username263SMBUser username yes The SMB username264TABLE265end266end267end268269describe '.dump_advanced_options' do270context 'when kerberos options are present' do271let(:advanced_module_options) do272[273*default_advanced_module_options,274*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),275]276end277278it 'returns the options as a table' do279expect(described_class.dump_advanced_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE280Name Current Setting Required Description281---- --------------- -------- -----------282DigestAlgorithm SHA256 yes The digest algorithm to use (Accepted: SHA1, SHA256)283VERBOSE false no Enable detailed status messages284WORKSPACE no Specify the workspace for this module285Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)286287288When Winrm::Auth is kerberos:289290Name Current Setting Required Description291---- --------------- -------- -----------292DomainControllerRhost no The resolvable rhost for the Domain Controller293KrbClockSkew 0s yes Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)294Winrm::Krb5Ccname no The ccache file to use for kerberos authentication295Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer296Winrm::Rhostname no The rhostname which is required for kerberos - the SPN297TABLE298end299end300301context 'when some options are grouped' do302let(:group_name) { 'group_name' }303let(:group_description) { 'Used for example reasons' }304let(:option_names) { %w[DigestAlgorithm RHOSTS SMBUser SMBDomain] }305let(:group) { Msf::OptionGroup.new(name: group_name, description: group_description, option_names: option_names) }306let(:aux_mod_with_grouped_options) do307mod = aux_mod_with_set_options.replicant308mod.options.add_group(group)309mod310end311312it 'should return the grouped options separate to the rest of the options' do313expect(described_class.dump_advanced_options(aux_mod_with_grouped_options, indent_string)).to match_table <<~TABLE314Name Current Setting Required Description315---- --------------- -------- -----------316VERBOSE false no Enable detailed status messages317WORKSPACE no Specify the workspace for this module318319320#{group_description}:321322Name Current Setting Required Description323---- --------------- -------- -----------324DigestAlgorithm SHA256 yes The digest algorithm to use (Accepted: SHA1, SHA256)325326TABLE327end328end329end330331describe '.dump_evasion_options' do332context 'when kerberos options are present' do333let(:evasion_module_options) do334[335*default_evasion_module_options,336*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),337]338end339340it 'returns the options as a table' do341expect(described_class.dump_evasion_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE342Name Current Setting Required Description343---- --------------- -------- -----------344EVASION_TEST_OPTION yes The evasion test option345Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)346347348When Winrm::Auth is kerberos:349350Name Current Setting Required Description351---- --------------- -------- -----------352DomainControllerRhost no The resolvable rhost for the Domain Controller353KrbClockSkew 0s yes Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)354Winrm::Krb5Ccname no The ccache file to use for kerberos authentication355Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer356Winrm::Rhostname no The rhostname which is required for kerberos - the SPN357TABLE358end359end360end361362describe '.dump_description' do363context 'when the module description is nil' do364it 'dumps the module description' do365mod = instance_double(366Msf::Module,367description: nil368)369370result = described_class.dump_description(mod, ' ')371expect(result).to match_table <<~TABLE372Description:373374TABLE375end376end377378context 'when the module description has no whitespace' do379it 'dumps the module description' do380mod = instance_double(381Msf::Module,382description: 'this is a module description'383)384385result = described_class.dump_description(mod, ' ')386expect(result).to match_table <<~TABLE387Description:388this is a module description389TABLE390end391end392393context 'when the module description is a single line' do394it 'dumps the module description' do395mod = instance_double(396Msf::Module,397description: %q{ This is a description; with module details etc. }398)399400result = described_class.dump_description(mod, ' ')401expect(result).to match_table <<~TABLE402Description:403This is a description; with module details etc.404405TABLE406end407end408409context 'when the first line has less preceding whitespace than the subsequent lines' do410it 'dumps the module description' do411mod = instance_double(412Msf::Module,413description: 'Listen for a connection. First, the port will need to be knocked from414the IP defined in KHOST. This IP will work as an authentication method415(you can spoof it with tools like hping). After that you could get your416shellcode from any IP. The socket will appear as "closed," thus helping to417hide the shellcode',418)419420result = described_class.dump_description(mod, ' ')421expect(result).to match_table <<~TABLE422Description:423Listen for a connection. First, the port will need to be knocked from424the IP defined in KHOST. This IP will work as an authentication method425(you can spoof it with tools like hping). After that you could get your426shellcode from any IP. The socket will appear as "closed," thus helping to427hide the shellcode428TABLE429end430end431432context 'when the first line has more whitespace than the subsequent lines' do433it 'dumps the module description' do434mod = instance_double(435Msf::Module,436description: %q{437Login credentials to the Motorola WR850G router with438firmware v4.03 can be obtained via a simple GET request439if issued while the administrator is logged in. A lot440more information is available through this request, but441you can get it all and more after logging in.442},443)444445result = described_class.dump_description(mod, ' ')446expect(result).to match_table <<~TABLE447Description:448Login credentials to the Motorola WR850G router with449firmware v4.03 can be obtained via a simple GET request450if issued while the administrator is logged in. A lot451more information is available through this request, but452you can get it all and more after logging in.453TABLE454end455end456457context 'when there are two blank lines in a row' do458it 'dumps the module description' do459mod = instance_double(460Msf::Module,461description: "Run a meterpreter server in Android.\n\nTunnel communication over HTTP"462)463464result = described_class.dump_description(mod, ' ')465expect(result).to match_table <<~TABLE466Description:467Run a meterpreter server in Android.468469Tunnel communication over HTTP470TABLE471end472end473474context 'when the module description spans multiple lines' do475it 'dumps the module description' do476mod = instance_double(477Msf::Module,478description: %q{479This is a description; with module details etc.480481Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis mattis lacus. Nam nisi diam, commodo id eu.482483This is a list of important items to consider:484- Item A485- Item B486- Item C487488}489)490491result = described_class.dump_description(mod, ' ')492expect(result).to match_table <<~TABLE493Description:494This is a description; with module details etc.495496Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis mattis lacus. Nam nisi diam, commodo id eu.497498This is a list of important items to consider:499- Item A500- Item B501- Item C502503TABLE504end505end506end507end508509510