Land #18865, Consolidate option dumps
This commit is contained in:
commit
a30a7f81e5
|
@ -74,9 +74,6 @@ jobs:
|
||||||
exclude:
|
exclude:
|
||||||
- { os: ubuntu-latest, ruby: '3.0' }
|
- { os: ubuntu-latest, ruby: '3.0' }
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
|
||||||
ruby: '3.1'
|
|
||||||
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DATASTORE_FALLBACKS=1'
|
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
ruby: '3.1'
|
ruby: '3.1'
|
||||||
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DEFER_MODULE_LOADS=1'
|
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DEFER_MODULE_LOADS=1'
|
||||||
|
|
|
@ -568,116 +568,85 @@ class ReadableText
|
||||||
# @param indent [String] the indentation to use.
|
# @param indent [String] the indentation to use.
|
||||||
# @param missing [Boolean] dump only empty required options.
|
# @param missing [Boolean] dump only empty required options.
|
||||||
# @return [String] the string form of the information.
|
# @return [String] the string form of the information.
|
||||||
def self.dump_options(mod, indent = '', missing = false)
|
def self.dump_options(mod, indent = '', missing = false, advanced: false, evasion: false)
|
||||||
options = mod.options.map { |_name, option| option }
|
filtered_options = mod.options.values.select { |opt| opt.advanced? == advanced && opt.evasion? == evasion }
|
||||||
options_grouped_by_conditions = options.group_by(&:conditions)
|
|
||||||
|
|
||||||
options_with_conditions = ''.dup
|
options_grouped_by_conditions = filtered_options.group_by(&:conditions)
|
||||||
options_without_conditions = ''.dup
|
|
||||||
|
|
||||||
options_grouped_by_conditions.each do |conditions, options|
|
option_tables = []
|
||||||
tbl = Rex::Text::Table.new(
|
|
||||||
'Indent' => indent.length,
|
|
||||||
'Columns' =>
|
|
||||||
[
|
|
||||||
'Name',
|
|
||||||
'Current Setting',
|
|
||||||
'Required',
|
|
||||||
'Description'
|
|
||||||
])
|
|
||||||
|
|
||||||
options.sort_by(&:name).each do |opt|
|
options_grouped_by_conditions.sort.each do |conditions, options|
|
||||||
name = opt.name
|
tbl = options_table(missing, mod, options, indent)
|
||||||
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
|
|
||||||
val = mod.datastore[name]
|
|
||||||
else
|
|
||||||
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
next if (opt.advanced?)
|
next if tbl.rows.empty?
|
||||||
next if (opt.evasion?)
|
|
||||||
next if (missing && opt.valid?(val))
|
|
||||||
|
|
||||||
desc = opt.desc.dup
|
|
||||||
|
|
||||||
# Hint at RPORT proto by regexing mixins
|
|
||||||
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
|
|
||||||
mod.class.included_modules.each do |m|
|
|
||||||
case m.name
|
|
||||||
when /tcp/i, /HttpClient$/
|
|
||||||
desc << ' (TCP)'
|
|
||||||
break
|
|
||||||
when /udp/i
|
|
||||||
desc << ' (UDP)'
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", desc ]
|
|
||||||
end
|
|
||||||
|
|
||||||
next if conditions.any? && tbl.rows.empty?
|
|
||||||
|
|
||||||
if conditions.any?
|
if conditions.any?
|
||||||
options_with_conditions << "\n\n#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
|
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"
|
||||||
options_with_conditions << tbl.to_s
|
|
||||||
else
|
else
|
||||||
options_without_conditions << tbl.to_s
|
option_tables << tbl.to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
result = "#{options_without_conditions}#{options_with_conditions}"
|
result = option_tables.join("\n\n")
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Creates the table for the given module options
|
||||||
|
#
|
||||||
|
# @param missing [Boolean] dump only empty required options.
|
||||||
|
# @param mod [Msf::Module] the module.
|
||||||
|
# @param options [Array<Msf::OptBase>] The options to be added to the table
|
||||||
|
# @param indent [String] the indentation to use.
|
||||||
|
#
|
||||||
|
# @return [String] the string form of the table.
|
||||||
|
def self.options_table(missing, mod, options, indent)
|
||||||
|
tbl = Rex::Text::Table.new(
|
||||||
|
'Indent' => indent.length,
|
||||||
|
'Columns' =>
|
||||||
|
[
|
||||||
|
'Name',
|
||||||
|
'Current Setting',
|
||||||
|
'Required',
|
||||||
|
'Description'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
options.sort_by(&:name).each do |opt|
|
||||||
|
name = opt.name
|
||||||
|
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
|
||||||
|
val = mod.datastore[name]
|
||||||
|
else
|
||||||
|
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
||||||
|
end
|
||||||
|
next if (missing && opt.valid?(val))
|
||||||
|
|
||||||
|
desc = opt.desc.dup
|
||||||
|
|
||||||
|
# Hint at RPORT proto by regexing mixins
|
||||||
|
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
|
||||||
|
mod.class.included_modules.each do |m|
|
||||||
|
case m.name
|
||||||
|
when /tcp/i, /HttpClient$/
|
||||||
|
desc << ' (TCP)'
|
||||||
|
break
|
||||||
|
when /udp/i
|
||||||
|
desc << ' (UDP)'
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tbl << [name, opt.display_value(val), opt.required? ? "yes" : "no", desc]
|
||||||
|
end
|
||||||
|
tbl
|
||||||
|
end
|
||||||
|
|
||||||
# Dumps the advanced options associated with the supplied module.
|
# Dumps the advanced options associated with the supplied module.
|
||||||
#
|
#
|
||||||
# @param mod [Msf::Module] the module.
|
# @param mod [Msf::Module] the module.
|
||||||
# @param indent [String] the indentation to use.
|
# @param indent [String] the indentation to use.
|
||||||
# @return [String] the string form of the information.
|
# @return [String] the string form of the information.
|
||||||
def self.dump_advanced_options(mod, indent = '')
|
def self.dump_advanced_options(mod, indent = '')
|
||||||
options = mod.options.map { |_name, option| option }
|
return dump_options(mod, indent, advanced: true)
|
||||||
options_grouped_by_conditions = options.group_by(&:conditions)
|
|
||||||
|
|
||||||
options_with_conditions = ''.dup
|
|
||||||
options_without_conditions = ''.dup
|
|
||||||
|
|
||||||
options_grouped_by_conditions.each do |conditions, options|
|
|
||||||
tbl = Rex::Text::Table.new(
|
|
||||||
'Indent' => indent.length,
|
|
||||||
'Columns' =>
|
|
||||||
[
|
|
||||||
'Name',
|
|
||||||
'Current Setting',
|
|
||||||
'Required',
|
|
||||||
'Description'
|
|
||||||
])
|
|
||||||
|
|
||||||
options.sort_by(&:name).each do |opt|
|
|
||||||
next unless opt.advanced?
|
|
||||||
|
|
||||||
name = opt.name
|
|
||||||
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
|
|
||||||
val = mod.datastore[name]
|
|
||||||
else
|
|
||||||
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
|
||||||
end
|
|
||||||
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
|
|
||||||
end
|
|
||||||
|
|
||||||
next if conditions.any? && tbl.rows.empty?
|
|
||||||
|
|
||||||
if conditions.any?
|
|
||||||
options_with_conditions << "\n\n#{indent}Active when #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
|
|
||||||
options_with_conditions << tbl.to_s
|
|
||||||
else
|
|
||||||
options_without_conditions << tbl.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
result = "#{options_without_conditions}#{options_with_conditions}"
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Dumps the evasion options associated with the supplied module.
|
# Dumps the evasion options associated with the supplied module.
|
||||||
|
@ -686,46 +655,7 @@ class ReadableText
|
||||||
# @param indent [String] the indentation to use.
|
# @param indent [String] the indentation to use.
|
||||||
# @return [String] the string form of the information.
|
# @return [String] the string form of the information.
|
||||||
def self.dump_evasion_options(mod, indent = '')
|
def self.dump_evasion_options(mod, indent = '')
|
||||||
options = mod.options.map { |_name, option| option }
|
return dump_options(mod, indent, evasion: true)
|
||||||
options_grouped_by_conditions = options.group_by(&:conditions)
|
|
||||||
|
|
||||||
options_with_conditions = ''.dup
|
|
||||||
options_without_conditions = ''.dup
|
|
||||||
|
|
||||||
options_grouped_by_conditions.each do |conditions, options|
|
|
||||||
tbl = Rex::Text::Table.new(
|
|
||||||
'Indent' => indent.length,
|
|
||||||
'Columns' =>
|
|
||||||
[
|
|
||||||
'Name',
|
|
||||||
'Current Setting',
|
|
||||||
'Required',
|
|
||||||
'Description'
|
|
||||||
])
|
|
||||||
|
|
||||||
options.sort_by(&:name).each do |opt|
|
|
||||||
next unless opt.evasion?
|
|
||||||
|
|
||||||
name = opt.name
|
|
||||||
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
|
|
||||||
val = mod.datastore[name]
|
|
||||||
else
|
|
||||||
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
|
||||||
end
|
|
||||||
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
|
|
||||||
end
|
|
||||||
|
|
||||||
next if conditions.any? && tbl.rows.empty?
|
|
||||||
|
|
||||||
if conditions.any?
|
|
||||||
options_with_conditions << "\n\n#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
|
|
||||||
options_with_conditions << tbl.to_s
|
|
||||||
else
|
|
||||||
options_without_conditions << tbl.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
result = "#{options_without_conditions}#{options_with_conditions}"
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Dumps the references associated with the supplied module.
|
# Dumps the references associated with the supplied module.
|
||||||
|
|
|
@ -54,8 +54,15 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:default_evasion_module_options) do
|
||||||
|
[
|
||||||
|
Msf::OptInt.new('EVASION_TEST_OPTION', [ true, 'The evasion test option'])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
let(:module_options) { default_module_options }
|
let(:module_options) { default_module_options }
|
||||||
let(:advanced_module_options) { default_advanced_module_options }
|
let(:advanced_module_options) { default_advanced_module_options }
|
||||||
|
let(:evasion_module_options) { default_evasion_module_options }
|
||||||
|
|
||||||
# (see Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options#kerberos_auth_options)
|
# (see Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options#kerberos_auth_options)
|
||||||
def kerberos_auth_options(protocol:, auth_methods:)
|
def kerberos_auth_options(protocol:, auth_methods:)
|
||||||
|
@ -83,6 +90,7 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
mod = mod_klass.new
|
mod = mod_klass.new
|
||||||
mod.send(:register_options, module_options)
|
mod.send(:register_options, module_options)
|
||||||
mod.send(:register_advanced_options, advanced_module_options)
|
mod.send(:register_advanced_options, advanced_module_options)
|
||||||
|
mod.send(:register_evasion_options, evasion_module_options)
|
||||||
mock_framework = instance_double(::Msf::Framework, datastore: Msf::DataStore.new)
|
mock_framework = instance_double(::Msf::Framework, datastore: Msf::DataStore.new)
|
||||||
allow(mod).to receive(:framework).and_return(mock_framework)
|
allow(mod).to receive(:framework).and_return(mock_framework)
|
||||||
mod
|
mod
|
||||||
|
@ -105,7 +113,7 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
allow(Rex::Text::Table).to receive(:wrapped_tables?).and_return(true)
|
allow(Rex::Text::Table).to receive(:wrapped_tables?).and_return(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.dump_datastore', if: ENV['DATASTORE_FALLBACKS'] do
|
describe '.dump_datastore' do
|
||||||
context 'when the datastore is empty' do
|
context 'when the datastore is empty' do
|
||||||
it 'returns the datastore as a table' do
|
it 'returns the datastore as a table' do
|
||||||
expect(described_class.dump_datastore('Table name', Msf::DataStore.new, indent_length)).to match_table <<~TABLE
|
expect(described_class.dump_datastore('Table name', Msf::DataStore.new, indent_length)).to match_table <<~TABLE
|
||||||
|
@ -126,6 +134,7 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
Name Value
|
Name Value
|
||||||
---- -----
|
---- -----
|
||||||
DigestAlgorithm SHA256
|
DigestAlgorithm SHA256
|
||||||
|
EVASION_TEST_OPTION
|
||||||
FloatValue 5
|
FloatValue 5
|
||||||
NewOptionName
|
NewOptionName
|
||||||
OptionWithModuleDefault false
|
OptionWithModuleDefault false
|
||||||
|
@ -144,7 +153,7 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.dump_options', if: ENV['DATASTORE_FALLBACKS'] do
|
describe '.dump_options' do
|
||||||
context 'when missing is false' do
|
context 'when missing is false' do
|
||||||
it 'returns the options as a table' do
|
it 'returns the options as a table' do
|
||||||
expect(described_class.dump_options(aux_mod_with_set_options, indent_string, false)).to match_table <<~TABLE
|
expect(described_class.dump_options(aux_mod_with_set_options, indent_string, false)).to match_table <<~TABLE
|
||||||
|
@ -175,7 +184,7 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.dump_advanced_options', if: ENV['DATASTORE_FALLBACKS'] do
|
describe '.dump_advanced_options' do
|
||||||
context 'when kerberos options are present' do
|
context 'when kerberos options are present' do
|
||||||
let(:advanced_module_options) do
|
let(:advanced_module_options) do
|
||||||
[
|
[
|
||||||
|
@ -194,7 +203,37 @@ RSpec.describe Msf::Serializer::ReadableText do
|
||||||
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
|
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
|
||||||
|
|
||||||
|
|
||||||
Active when Winrm::Auth is kerberos:
|
When Winrm::Auth is kerberos:
|
||||||
|
|
||||||
|
Name Current Setting Required Description
|
||||||
|
---- --------------- -------- -----------
|
||||||
|
DomainControllerRhost no The resolvable rhost for the Domain Controller
|
||||||
|
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
|
||||||
|
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
|
||||||
|
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
|
||||||
|
TABLE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.dump_evasion_options' do
|
||||||
|
context 'when kerberos options are present' do
|
||||||
|
let(:evasion_module_options) do
|
||||||
|
[
|
||||||
|
*default_evasion_module_options,
|
||||||
|
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the options as a table' do
|
||||||
|
expect(described_class.dump_evasion_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE
|
||||||
|
Name Current Setting Required Description
|
||||||
|
---- --------------- -------- -----------
|
||||||
|
EVASION_TEST_OPTION yes The evasion test option
|
||||||
|
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
|
||||||
|
|
||||||
|
|
||||||
|
When Winrm::Auth is kerberos:
|
||||||
|
|
||||||
Name Current Setting Required Description
|
Name Current Setting Required Description
|
||||||
---- --------------- -------- -----------
|
---- --------------- -------- -----------
|
||||||
|
|
|
@ -83,7 +83,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Core do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#cmd_set', if: ENV['DATASTORE_FALLBACKS'] do
|
describe '#cmd_set' do
|
||||||
let(:mod) { nil }
|
let(:mod) { nil }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
@ -198,7 +198,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Core do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#cmd_unset', if: ENV['DATASTORE_FALLBACKS'] do
|
describe '#cmd_unset' do
|
||||||
let(:mod) { nil }
|
let(:mod) { nil }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
|
Loading…
Reference in New Issue