Skip to content

Commit a30a7f8

Browse files
Land #18865, Consolidate option dumps
2 parents 4fcb4a4 + e288592 commit a30a7f8

File tree

4 files changed

+104
-138
lines changed

4 files changed

+104
-138
lines changed

.github/workflows/verify.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ jobs:
7474
exclude:
7575
- { os: ubuntu-latest, ruby: '3.0' }
7676
include:
77-
- os: ubuntu-latest
78-
ruby: '3.1'
79-
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DATASTORE_FALLBACKS=1'
8077
- os: ubuntu-latest
8178
ruby: '3.1'
8279
test_cmd: 'bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content" MSF_FEATURE_DEFER_MODULE_LOADS=1'

lib/msf/base/serializer/readable_text.rb

Lines changed: 59 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -568,116 +568,85 @@ def self.dump_generic_module(mod, indent = '')
568568
# @param indent [String] the indentation to use.
569569
# @param missing [Boolean] dump only empty required options.
570570
# @return [String] the string form of the information.
571-
def self.dump_options(mod, indent = '', missing = false)
572-
options = mod.options.map { |_name, option| option }
573-
options_grouped_by_conditions = options.group_by(&:conditions)
571+
def self.dump_options(mod, indent = '', missing = false, advanced: false, evasion: false)
572+
filtered_options = mod.options.values.select { |opt| opt.advanced? == advanced && opt.evasion? == evasion }
574573

575-
options_with_conditions = ''.dup
576-
options_without_conditions = ''.dup
574+
options_grouped_by_conditions = filtered_options.group_by(&:conditions)
577575

578-
options_grouped_by_conditions.each do |conditions, options|
579-
tbl = Rex::Text::Table.new(
580-
'Indent' => indent.length,
581-
'Columns' =>
582-
[
583-
'Name',
584-
'Current Setting',
585-
'Required',
586-
'Description'
587-
])
588-
589-
options.sort_by(&:name).each do |opt|
590-
name = opt.name
591-
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
592-
val = mod.datastore[name]
593-
else
594-
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
595-
end
576+
option_tables = []
596577

597-
next if (opt.advanced?)
598-
next if (opt.evasion?)
599-
next if (missing && opt.valid?(val))
600-
601-
desc = opt.desc.dup
602-
603-
# Hint at RPORT proto by regexing mixins
604-
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
605-
mod.class.included_modules.each do |m|
606-
case m.name
607-
when /tcp/i, /HttpClient$/
608-
desc << ' (TCP)'
609-
break
610-
when /udp/i
611-
desc << ' (UDP)'
612-
break
613-
end
614-
end
615-
end
616-
617-
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", desc ]
618-
end
578+
options_grouped_by_conditions.sort.each do |conditions, options|
579+
tbl = options_table(missing, mod, options, indent)
619580

620-
next if conditions.any? && tbl.rows.empty?
581+
next if tbl.rows.empty?
621582

622583
if conditions.any?
623-
options_with_conditions << "\n\n#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
624-
options_with_conditions << tbl.to_s
584+
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"
625585
else
626-
options_without_conditions << tbl.to_s
586+
option_tables << tbl.to_s
627587
end
628588
end
629589

630-
result = "#{options_without_conditions}#{options_with_conditions}"
590+
result = option_tables.join("\n\n")
631591
result
632592
end
633593

634-
# Dumps the advanced options associated with the supplied module.
594+
# Creates the table for the given module options
635595
#
596+
# @param missing [Boolean] dump only empty required options.
636597
# @param mod [Msf::Module] the module.
598+
# @param options [Array<Msf::OptBase>] The options to be added to the table
637599
# @param indent [String] the indentation to use.
638-
# @return [String] the string form of the information.
639-
def self.dump_advanced_options(mod, indent = '')
640-
options = mod.options.map { |_name, option| option }
641-
options_grouped_by_conditions = options.group_by(&:conditions)
642-
643-
options_with_conditions = ''.dup
644-
options_without_conditions = ''.dup
645-
646-
options_grouped_by_conditions.each do |conditions, options|
647-
tbl = Rex::Text::Table.new(
648-
'Indent' => indent.length,
649-
'Columns' =>
650-
[
651-
'Name',
652-
'Current Setting',
653-
'Required',
654-
'Description'
655-
])
656-
657-
options.sort_by(&:name).each do |opt|
658-
next unless opt.advanced?
659-
660-
name = opt.name
661-
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
662-
val = mod.datastore[name]
663-
else
664-
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
600+
#
601+
# @return [String] the string form of the table.
602+
def self.options_table(missing, mod, options, indent)
603+
tbl = Rex::Text::Table.new(
604+
'Indent' => indent.length,
605+
'Columns' =>
606+
[
607+
'Name',
608+
'Current Setting',
609+
'Required',
610+
'Description'
611+
]
612+
)
613+
options.sort_by(&:name).each do |opt|
614+
name = opt.name
615+
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
616+
val = mod.datastore[name]
617+
else
618+
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
619+
end
620+
next if (missing && opt.valid?(val))
621+
622+
desc = opt.desc.dup
623+
624+
# Hint at RPORT proto by regexing mixins
625+
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
626+
mod.class.included_modules.each do |m|
627+
case m.name
628+
when /tcp/i, /HttpClient$/
629+
desc << ' (TCP)'
630+
break
631+
when /udp/i
632+
desc << ' (UDP)'
633+
break
634+
end
665635
end
666-
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
667636
end
668637

669-
next if conditions.any? && tbl.rows.empty?
670-
671-
if conditions.any?
672-
options_with_conditions << "\n\n#{indent}Active when #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
673-
options_with_conditions << tbl.to_s
674-
else
675-
options_without_conditions << tbl.to_s
676-
end
638+
tbl << [name, opt.display_value(val), opt.required? ? "yes" : "no", desc]
677639
end
640+
tbl
641+
end
678642

679-
result = "#{options_without_conditions}#{options_with_conditions}"
680-
result
643+
# Dumps the advanced options associated with the supplied module.
644+
#
645+
# @param mod [Msf::Module] the module.
646+
# @param indent [String] the indentation to use.
647+
# @return [String] the string form of the information.
648+
def self.dump_advanced_options(mod, indent = '')
649+
return dump_options(mod, indent, advanced: true)
681650
end
682651

683652
# Dumps the evasion options associated with the supplied module.
@@ -686,46 +655,7 @@ def self.dump_advanced_options(mod, indent = '')
686655
# @param indent [String] the indentation to use.
687656
# @return [String] the string form of the information.
688657
def self.dump_evasion_options(mod, indent = '')
689-
options = mod.options.map { |_name, option| option }
690-
options_grouped_by_conditions = options.group_by(&:conditions)
691-
692-
options_with_conditions = ''.dup
693-
options_without_conditions = ''.dup
694-
695-
options_grouped_by_conditions.each do |conditions, options|
696-
tbl = Rex::Text::Table.new(
697-
'Indent' => indent.length,
698-
'Columns' =>
699-
[
700-
'Name',
701-
'Current Setting',
702-
'Required',
703-
'Description'
704-
])
705-
706-
options.sort_by(&:name).each do |opt|
707-
next unless opt.evasion?
708-
709-
name = opt.name
710-
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
711-
val = mod.datastore[name]
712-
else
713-
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
714-
end
715-
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
716-
end
717-
718-
next if conditions.any? && tbl.rows.empty?
719-
720-
if conditions.any?
721-
options_with_conditions << "\n\n#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n"
722-
options_with_conditions << tbl.to_s
723-
else
724-
options_without_conditions << tbl.to_s
725-
end
726-
end
727-
result = "#{options_without_conditions}#{options_with_conditions}"
728-
result
658+
return dump_options(mod, indent, evasion: true)
729659
end
730660

731661
# Dumps the references associated with the supplied module.

spec/lib/msf/base/serializer/readable_text_spec.rb

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,15 @@
5454
]
5555
end
5656

57+
let(:default_evasion_module_options) do
58+
[
59+
Msf::OptInt.new('EVASION_TEST_OPTION', [ true, 'The evasion test option'])
60+
]
61+
end
62+
5763
let(:module_options) { default_module_options }
5864
let(:advanced_module_options) { default_advanced_module_options }
65+
let(:evasion_module_options) { default_evasion_module_options }
5966

6067
# (see Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options#kerberos_auth_options)
6168
def kerberos_auth_options(protocol:, auth_methods:)
@@ -83,6 +90,7 @@ def initialize
8390
mod = mod_klass.new
8491
mod.send(:register_options, module_options)
8592
mod.send(:register_advanced_options, advanced_module_options)
93+
mod.send(:register_evasion_options, evasion_module_options)
8694
mock_framework = instance_double(::Msf::Framework, datastore: Msf::DataStore.new)
8795
allow(mod).to receive(:framework).and_return(mock_framework)
8896
mod
@@ -105,7 +113,7 @@ def initialize
105113
allow(Rex::Text::Table).to receive(:wrapped_tables?).and_return(true)
106114
end
107115

108-
describe '.dump_datastore', if: ENV['DATASTORE_FALLBACKS'] do
116+
describe '.dump_datastore' do
109117
context 'when the datastore is empty' do
110118
it 'returns the datastore as a table' do
111119
expect(described_class.dump_datastore('Table name', Msf::DataStore.new, indent_length)).to match_table <<~TABLE
@@ -126,6 +134,7 @@ def initialize
126134
Name Value
127135
---- -----
128136
DigestAlgorithm SHA256
137+
EVASION_TEST_OPTION
129138
FloatValue 5
130139
NewOptionName
131140
OptionWithModuleDefault false
@@ -144,7 +153,7 @@ def initialize
144153
end
145154
end
146155

147-
describe '.dump_options', if: ENV['DATASTORE_FALLBACKS'] do
156+
describe '.dump_options' do
148157
context 'when missing is false' do
149158
it 'returns the options as a table' do
150159
expect(described_class.dump_options(aux_mod_with_set_options, indent_string, false)).to match_table <<~TABLE
@@ -175,7 +184,7 @@ def initialize
175184
end
176185
end
177186

178-
describe '.dump_advanced_options', if: ENV['DATASTORE_FALLBACKS'] do
187+
describe '.dump_advanced_options' do
179188
context 'when kerberos options are present' do
180189
let(:advanced_module_options) do
181190
[
@@ -194,7 +203,37 @@ def initialize
194203
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
195204
196205
197-
Active when Winrm::Auth is kerberos:
206+
When Winrm::Auth is kerberos:
207+
208+
Name Current Setting Required Description
209+
---- --------------- -------- -----------
210+
DomainControllerRhost no The resolvable rhost for the Domain Controller
211+
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
212+
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
213+
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
214+
TABLE
215+
end
216+
end
217+
end
218+
219+
describe '.dump_evasion_options' do
220+
context 'when kerberos options are present' do
221+
let(:evasion_module_options) do
222+
[
223+
*default_evasion_module_options,
224+
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
225+
]
226+
end
227+
228+
it 'returns the options as a table' do
229+
expect(described_class.dump_evasion_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE
230+
Name Current Setting Required Description
231+
---- --------------- -------- -----------
232+
EVASION_TEST_OPTION yes The evasion test option
233+
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
234+
235+
236+
When Winrm::Auth is kerberos:
198237
199238
Name Current Setting Required Description
200239
---- --------------- -------- -----------

spec/lib/msf/ui/console/command_dispatcher/core_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def set_and_test_variable(name, framework_value, module_value, framework_re, mod
8383
end
8484
end
8585

86-
describe '#cmd_set', if: ENV['DATASTORE_FALLBACKS'] do
86+
describe '#cmd_set' do
8787
let(:mod) { nil }
8888

8989
before(:each) do
@@ -198,7 +198,7 @@ def initialize
198198
end
199199
end
200200

201-
describe '#cmd_unset', if: ENV['DATASTORE_FALLBACKS'] do
201+
describe '#cmd_unset' do
202202
let(:mod) { nil }
203203

204204
before(:each) do

0 commit comments

Comments
 (0)