Skip to content

Commit

Permalink
Fix edgecase in Meterpreter job persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed May 8, 2024
1 parent fd10f4d commit a37d05b
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 15 deletions.
2 changes: 1 addition & 1 deletion lib/msf/base/serializer/readable_text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = Def
persist_list.each do |e|
handler_ctx = framework.jobs[job_id.to_s].ctx[1]
if handler_ctx && handler_ctx.respond_to?(:datastore)
row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore
row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore.to_h
end
end

Expand Down
12 changes: 8 additions & 4 deletions lib/msf/base/simple/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ module Module
def _import_extra_options(opts)
# If options were supplied, import them into the payload's
# datastore
if (opts['Options'])
self.datastore.import_options_from_hash(opts['Options'])
elsif (opts['OptionStr'])
self.datastore.import_options_from_s(opts['OptionStr'])
if (value = opts['Options'])
if value.is_a?(String)
self.datastore.import_options_from_s(value)
else
self.datastore.import_options_from_hash(value)
end
elsif (value = opts['OptionStr'])
self.datastore.import_options_from_s(value)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/msf/core/opt_meterpreter_debug_logging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def self.parse_logging_options(value)
result = {}
errors = []

return result if value.nil?
value = value.to_s.strip
return result if value.empty?

value = value.strip
# Match 'rpath:./file', 'rpath:/file', and drive letters e.g. 'rpath:C:/file'
rpath_regex = %r{^rpath:((\.?/\p{ASCII}+)|(\p{ASCII}:/\p{ASCII}+))}i

Expand Down
11 changes: 6 additions & 5 deletions lib/msf/ui/console/command_dispatcher/jobs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,13 @@ def cmd_jobs(*args)
end

# Remove persistence by job id.
job_list.map(&:to_s).each do |job|
if framework.jobs.key?(job)
ctx_1 = framework.jobs[job.to_s].ctx[1]
job_list.map(&:to_s).each do |job_id|
job_id = job_id.to_i < 0 ? framework.jobs.keys[job_id.to_i] : job_id
if framework.jobs.key?(job_id)
ctx_1 = framework.jobs[job_id.to_s].ctx[1]
next if ctx_1.nil? || !ctx_1.respond_to?(:datastore) # next if no payload context in the job
payload_option = ctx_1.datastore
persist_list.delete_if{|pjob|pjob['mod_options']['Options'] == payload_option}
persist_list.delete_if{|pjob|pjob['mod_options']['Options'] == payload_option.to_h}
end
end
# Write persist job back to config file.
Expand Down Expand Up @@ -257,7 +258,7 @@ def add_persist_job(job_id)

payload_opts = {
'Payload' => payload.refname,
'Options' => payload.datastore,
'Options' => payload.datastore.to_h,
'RunAsJob' => true
}

Expand Down
18 changes: 16 additions & 2 deletions lib/msf/ui/console/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,23 @@ def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {
if restore_handlers
print_status("Starting persistent handler(s)...")

restore_handlers.each do |handler_opts|
restore_handlers.each.with_index do |handler_opts, index|
handler = framework.modules.create(handler_opts['mod_name'])
handler.exploit_simple(handler_opts['mod_options'])
handler.init_ui(self.input, self.output)
replicant_handler = nil
handler.exploit_simple(handler_opts['mod_options']) do |yielded_replicant_handler|
replicant_handler = yielded_replicant_handler
end

if replicant_handler.nil? || replicant_handler.error
print_status("Failed to start persistent payload handler ##{index} (#{handler_opts['mod_name']})")
next
end

if replicant_handler.error.nil?
job_id = handler.job_id
print_status "Persistent payload handler started as Job #{job_id}"
end
end
end

Expand Down
18 changes: 17 additions & 1 deletion spec/lib/msf/core/opt_meterpreter_debug_logging_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
{ value: 'rpath:C:/log.txt', normalized: 'rpath:C:/log.txt' },
{ value: 'rpath:/tmp/log.txt', normalized: 'rpath:/tmp/log.txt' },
{ value: 'rpath:./log.log', normalized: 'rpath:./log.log' },
{ value: ' rpath:./log.log ', normalized: ' rpath:./log.log ' }
{ value: ' rpath:./log.log ', normalized: ' rpath:./log.log ' },
{ value: '', normalized: '' },
{ value: ' ', normalized: ' ' }
]
invalid_values = [
{ value: 'rpath', normalized: 'rpath' },
Expand All @@ -17,4 +19,18 @@
]

it_behaves_like 'an option', valid_values, invalid_values, 'meterpreterdebuglogging'

describe '.parse_logging_options' do
[
{ value: nil, expected: {} },
{ value: '', expected: {} },
{ value: ' ', expected: {} },
{ value: 'rpath:./file', expected: { rpath: './file' } },
{ value: ' rpath:C:/file ', expected: { rpath: 'C:/file' } },
].each do |test|
it "parses #{test[:value]} as #{test[:expected]}" do
expect(described_class.parse_logging_options(test[:value])).to eq test[:expected]
end
end
end
end

0 comments on commit a37d05b

Please sign in to comment.