Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix edgecase in Meterpreter job persistence #19002

Conversation

adfoster-r7
Copy link
Contributor

@adfoster-r7 adfoster-r7 commented Mar 23, 2024

closes #18995

Verification

  1. Verify jobs can be persisted
use exploit/multi/handler
set payload linux/x64/meterpreter/reverse_tcp
setg lhost ip
run -j
jobs -p job_id
exit
msfconsole
jobs

Ensure the job is available on console boot jobs

  1. Verify that jobs -v correctly detects when a job is persisted
  2. Verify that the job is removed from being persisted when using jobs -k 1 and with a negative index jobs -k -1

elsif (opts['OptionStr'])
self.datastore.import_options_from_s(opts['OptionStr'])
if (value = opts['Options'])
if value.is_a?(String)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows persisted jobs to still be loaded correctly, i.e. the scenario of Options being a string instead of hash

@@ -257,7 +257,7 @@ def add_persist_job(job_id)

payload_opts = {
'Payload' => payload.refname,
'Options' => payload.datastore,
'Options' => payload.datastore.to_h,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serializing payload.datastore as JSON led to a string being saved, this allows a hash to be persisted instead

@@ -208,7 +208,21 @@ def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {

restore_handlers.each do |handler_opts|
handler = framework.modules.create(handler_opts['mod_name'])
handler.exploit_simple(handler_opts['mod_options'])
handler.init_ui(self.input, self.output)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wiring up init_ui allows the user to have the job startup error messages logged to the console instead of them silently being ignored - which immediately showed the original hidden error

Before: No errors logged

After: Errors logged

bundle exec ruby ./msfconsole --quiet
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
[*] Starting persistent handler(s)...
[-] Msf::OptionValidateError One or more options failed to validate: MeterpreterDebugLogging.
[*] Failed to start persistent payload handler #0 (exploit/multi/handler)
[-] Msf::OptionValidateError One or more options failed to validate: MeterpreterDebugLogging.
[*] Failed to start persistent payload handler #1 (exploit/multi/handler)
msf6 exploit(multi/handler) >

@@ -17,4 +19,18 @@
]

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

describe '.parse_logging_options' do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests to verify the handling of MeterpreterDebugLogging being set to an empty string and causing validation errors is fixed, because the to_h method persists nil as the empty string "":

# Override Hash's to_h method so we can include the original case of each key
# (failing to do this breaks a number of places in framework and pro that use
# serialized datastores)
def to_h
datastore_hash = {}
self.keys.each do |k|
datastore_hash[k.to_s] = self[k].to_s
end
datastore_hash
end

@adfoster-r7 adfoster-r7 marked this pull request as draft April 8, 2024 11:20
@adfoster-r7 adfoster-r7 force-pushed the fix-edgecase-in-meterpreter-job-persistence branch from 9b4fb8e to d6eb8e5 Compare May 8, 2024 15:19
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pattern from: #15492

@adfoster-r7 adfoster-r7 marked this pull request as ready for review May 8, 2024 15:30
@adfoster-r7 adfoster-r7 force-pushed the fix-edgecase-in-meterpreter-job-persistence branch 2 times, most recently from a37d05b to 8f1472f Compare May 14, 2024 14:39
@adfoster-r7 adfoster-r7 force-pushed the fix-edgecase-in-meterpreter-job-persistence branch from 8f1472f to 0bba494 Compare May 16, 2024 10:17
@sjanusz-r7
Copy link
Contributor

I followed the setup instructions with setting up a multi/handler.

Before

Job does not survive a reboot of Console

bundle exec './msfconsole -q'
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Starting persistent handler(s)...
msf6 auxiliary(scanner/smb/smb_lookupsid) > jobs

Jobs
====

No active jobs.

After

Job persists

bundle exec './msfconsole -q'
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Starting persistent handler(s)...
[*] Persistent payload handler started as Job 0
[*] Started reverse TCP handler on 192.168.112.1:4444 
msf6 auxiliary(scanner/smb/smb_lookupsid) > jobs

Jobs
====

  Id  Name                    Payload                            Payload opts
  --  ----                    -------                            ------------
  0   Exploit: multi/handler  linux/x64/meterpreter/reverse_tcp  tcp://192.168.112.1:4444

msf6 auxiliary(scanner/smb/smb_lookupsid) > jobs -v

Jobs
====

  Id  Name                    Payload                            Payload opts              URIPATH  Start Time                 Handler opts  Persist
  --  ----                    -------                            ------------              -------  ----------                 ------------  -------
  0   Exploit: multi/handler  linux/x64/meterpreter/reverse_tcp  tcp://192.168.112.1:4444           2024-05-17 14:02:58 +0100                true

@sjanusz-r7 sjanusz-r7 merged commit 28396ff into rapid7:master May 17, 2024
47 checks passed
@sjanusz-r7 sjanusz-r7 added the rn-fix release notes fix label May 17, 2024
@sjanusz-r7
Copy link
Contributor

Release Notes

Fixed persistent jobs not working when rebooting MSF console.

@adfoster-r7 adfoster-r7 mentioned this pull request May 17, 2024
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rn-fix release notes fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Job persist function doesn't seem to work
2 participants