Skip to content

Commit 0bba494

Browse files
committed
Fix edgecase in Meterpreter job persistence
1 parent e3fdfd6 commit 0bba494

File tree

6 files changed

+50
-15
lines changed

6 files changed

+50
-15
lines changed

lib/msf/base/serializer/readable_text.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = Def
10611061
persist_list.each do |e|
10621062
handler_ctx = framework.jobs[job_id.to_s].ctx[1]
10631063
if handler_ctx && handler_ctx.respond_to?(:datastore)
1064-
row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore
1064+
row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore.to_h
10651065
end
10661066
end
10671067

lib/msf/base/simple/module.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ module Module
1818
def _import_extra_options(opts)
1919
# If options were supplied, import them into the payload's
2020
# datastore
21-
if (opts['Options'])
22-
self.datastore.import_options_from_hash(opts['Options'])
23-
elsif (opts['OptionStr'])
24-
self.datastore.import_options_from_s(opts['OptionStr'])
21+
if (value = opts['Options'])
22+
if value.is_a?(String)
23+
self.datastore.import_options_from_s(value)
24+
else
25+
self.datastore.import_options_from_hash(value)
26+
end
27+
elsif (value = opts['OptionStr'])
28+
self.datastore.import_options_from_s(value)
2529
end
2630
end
2731

lib/msf/core/opt_meterpreter_debug_logging.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ def self.parse_logging_options(value)
3939
result = {}
4040
errors = []
4141

42-
return result if value.nil?
42+
value = value.to_s.strip
43+
return result if value.empty?
4344

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

lib/msf/ui/console/command_dispatcher/jobs.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,13 @@ def cmd_jobs(*args)
213213
end
214214

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

258259
payload_opts = {
259260
'Payload' => payload.refname,
260-
'Options' => payload.datastore,
261+
'Options' => payload.datastore.to_h,
261262
'RunAsJob' => true
262263
}
263264

lib/msf/ui/console/driver.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,23 @@ def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {
206206
if restore_handlers
207207
print_status("Starting persistent handler(s)...")
208208

209-
restore_handlers.each do |handler_opts|
209+
restore_handlers.each.with_index do |handler_opts, index|
210210
handler = framework.modules.create(handler_opts['mod_name'])
211-
handler.exploit_simple(handler_opts['mod_options'])
211+
handler.init_ui(self.input, self.output)
212+
replicant_handler = nil
213+
handler.exploit_simple(handler_opts['mod_options']) do |yielded_replicant_handler|
214+
replicant_handler = yielded_replicant_handler
215+
end
216+
217+
if replicant_handler.nil? || replicant_handler.error
218+
print_status("Failed to start persistent payload handler ##{index} (#{handler_opts['mod_name']})")
219+
next
220+
end
221+
222+
if replicant_handler.error.nil?
223+
job_id = handler.job_id
224+
print_status "Persistent payload handler started as Job #{job_id}"
225+
end
212226
end
213227
end
214228

spec/lib/msf/core/opt_meterpreter_debug_logging_spec.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
{ value: 'rpath:C:/log.txt', normalized: 'rpath:C:/log.txt' },
88
{ value: 'rpath:/tmp/log.txt', normalized: 'rpath:/tmp/log.txt' },
99
{ value: 'rpath:./log.log', normalized: 'rpath:./log.log' },
10-
{ value: ' rpath:./log.log ', normalized: ' rpath:./log.log ' }
10+
{ value: ' rpath:./log.log ', normalized: ' rpath:./log.log ' },
11+
{ value: '', normalized: '' },
12+
{ value: ' ', normalized: ' ' }
1113
]
1214
invalid_values = [
1315
{ value: 'rpath', normalized: 'rpath' },
@@ -17,4 +19,18 @@
1719
]
1820

1921
it_behaves_like 'an option', valid_values, invalid_values, 'meterpreterdebuglogging'
22+
23+
describe '.parse_logging_options' do
24+
[
25+
{ value: nil, expected: {} },
26+
{ value: '', expected: {} },
27+
{ value: ' ', expected: {} },
28+
{ value: 'rpath:./file', expected: { rpath: './file' } },
29+
{ value: ' rpath:C:/file ', expected: { rpath: 'C:/file' } },
30+
].each do |test|
31+
it "parses #{test[:value]} as #{test[:expected]}" do
32+
expect(described_class.parse_logging_options(test[:value])).to eq test[:expected]
33+
end
34+
end
35+
end
2036
end

0 commit comments

Comments
 (0)