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

Extract reusable core session commands #18516

Merged
merged 5 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions lib/msf/base/sessions/command_shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CommandShell
include Rex::Ui::Text::Resource

@@irb_opts = Rex::Parser::Arguments.new(
'-h' => [false, 'Help menu.' ],
['-h', '--help'] => [false, 'Help menu.' ],
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
['-h', '--help'] => [false, 'Help menu.' ],
['-h', '--help'] => [false, 'Help menu.'],

'-e' => [true, 'Expression to evaluate.']
)

Expand Down Expand Up @@ -235,35 +235,35 @@ def cmd_sessions_help
end

def cmd_sessions(*args)
if args.length.zero? || args[0].to_i <= 0
# No args
if args.length != 1
print_status "Wrong number of arguments expected: 1, received: #{args.length}"
return cmd_sessions_help
end

if args.length == 1 && (args[1] == '-h' || args[1] == 'help')
# One arg, and args[1] => '-h' '-H' 'help'
if args[0] == '-h' || args[0] == '--help'
return cmd_sessions_help
end

if args.length != 1
# More than one argument
session_id = args[0].to_i
if session_id <= 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we check if this works the same as before when provided with negative session ids?

Copy link
Contributor

Choose a reason for hiding this comment

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

@dwelch-r7 Is there anything actionable here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, the previous implementation didn't accept negative session IDs so it's a nice to have but I don't think we should block the PR on it

print_status 'Invalid session id'
return cmd_sessions_help
end

if args[0].to_s == self.name.to_s
if session_id == self.sid
# Src == Dst
print_status("Session #{self.name} is already interactive.")
else
print_status("Backgrounding session #{self.name}...")
# store the next session id so that it can be referenced as soon
# as this session is no longer interacting
self.next_session = args[0]
self.next_session = session_id
self.interacting = false
end
end

def cmd_resource(*args)
if args.empty?
if args.empty? || args[0] == '-h' || args[0] == '--help'
cmd_resource_help
return false
end
Expand Down Expand Up @@ -320,9 +320,9 @@ def cmd_shell_help()
end

def cmd_shell(*args)
if args.length == 1 && (args[1] == '-h' || args[1] == 'help')
# One arg, and args[1] => '-h' '-H' 'help'
return cmd_sessions_help
if args.length == 1 && (args[0] == '-h' || args[0] == '--help')
# One arg, and args[0] => '-h' '--help'
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# One arg, and args[0] => '-h' '--help'

I don't think this comment is necessary.

return cmd_shell_help
end

if platform == 'windows'
Expand Down Expand Up @@ -570,7 +570,7 @@ def cmd_pry_help
# Open the Pry debugger on the current session
#
def cmd_pry(*args)
if args.include?('-h')
if args.include?('-h') || args.include?('--help')
cmd_pry_help
return
end
Expand Down
9 changes: 9 additions & 0 deletions lib/msf/base/sessions/meterpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ def initialize(rstream, opts={})
self.console = Rex::Post::Meterpreter::Ui::Console.new(self)
end

def exit
begin
self.core.shutdown
rescue StandardError
nil
end
self.shutdown_passive_dispatcher
self.console.stop
end
#
# Returns the session type as being 'meterpreter'.
#
Expand Down
236 changes: 236 additions & 0 deletions lib/msf/ui/console/command_dispatcher/session.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# frozen_string_literal: true

module Msf
module Ui
module Console
module CommandDispatcher
module Session
include Rex::Ui::Text::DispatcherShell::CommandDispatcher

@@irb_opts = Rex::Parser::Arguments.new(
%w[-h --help] => [false, 'Help menu.' ],
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
%w[-h --help] => [false, 'Help menu.' ],
%w[-h --help] => [false, 'Help menu.'],

'-e' => [true, 'Expression to evaluate.']
)
def commands
{
'?' => 'Help menu',
'background' => 'Backgrounds the current session',
'bg' => 'Alias for background',
'exit' => 'Terminate the session',
'help' => 'Help menu',
'irb' => 'Open an interactive Ruby shell on the current session',
'pry' => 'Open the Pry debugger on the current session',
'quit' => 'Terminate the session',
'resource' => 'Run the commands stored in a file',
'uuid' => 'Get the UUID for the current session',
'sessions' => 'Quickly switch to another session'
}
end

def cmd_background_help
print_line('Usage: background')
print_line
print_line('Stop interacting with this session and return to the parent prompt')
print_line
end

def cmd_background(*args)
if args.include?('-h') || args.include?('--help')
cmd_background_help
return
end
print_status("Backgrounding session #{client.name}...")
client.interacting = false
end

alias cmd_bg cmd_background
alias cmd_bg_help cmd_background_help

#
# Terminates the session.
#
def cmd_exit(*args)
print_status("Shutting down session: #{client.sid}")
client.exit
end

alias cmd_quit cmd_exit
Comment on lines +49 to +57
Copy link
Contributor

Choose a reason for hiding this comment

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

A session can be terminated, exit'ed, shut down and quit'ted. It would be nice to reduce the amount of terms for this action I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't necessarily disagree but I don't think it belongs in this PR, this is just a bit of a refactor and moving things around, the intention is not to alter the way someone would interact with a session here


def cmd_irb_help
print_line('Usage: irb')
print_line
print_line('Open an interactive Ruby shell on the current session.')
print @@irb_opts.usage
end

def cmd_irb_tabs(str, words)
return [] if words.length > 1

@@irb_opts.option_keys
end

#
# Open an interactive Ruby shell on the current session
#
def cmd_irb(*args)
expressions = []

# Parse the command options
@@irb_opts.parse(args) do |opt, _idx, val|
case opt
when '-e'
expressions << val
when '-h', '--help'
return cmd_irb_help
end
end

session = client
framework = client.framework

if expressions.empty?
print_status('Starting IRB shell...')
print_status("You are in the \"client\" (session) object\n")
framework.history_manager.with_context(name: :irb) do
Rex::Ui::Text::IrbShell.new(client).run
end
else
# XXX: No vprint_status here
if framework.datastore['VERBOSE'].to_s == 'true'
print_status("You are executing expressions in #{binding.receiver}")
end

expressions.each { |expression| eval(expression, binding) }
end
end

def cmd_pry_help
print_line 'Usage: pry'
print_line
print_line 'Open the Pry debugger on the current session.'
print_line
end

#
# Open the Pry debugger on the current session
#
def cmd_pry(*args)
if args.include?('-h') || args.include?('--help')
cmd_pry_help
return
end

begin
require 'pry'
rescue LoadError
print_error('Failed to load Pry, try "gem install pry"')
return
end

print_status('Starting Pry shell...')
print_status("You are in the \"client\" (session) object\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
print_status("You are in the \"client\" (session) object\n")
print_status('You are in the "client" (session) object')
print_line


Pry.config.history_load = false
client.framework.history_manager.with_context(history_file: Msf::Config.pry_history, name: :pry) do
client.pry
end
end

def cmd_sessions_help
print_line('Usage: sessions <id>')
print_line
print_line('Interact with a different session Id.')
print_line('This works the same as calling this from the MSF shell: sessions -i <session id>')
print_line
end

def cmd_sessions(*args)
if args.empty? || args[0].to_i == 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if args.empty? || args[0].to_i == 0
if args.empty? || args[0].to_i == 0 || args.include?('-h') || args.include?('--help')

cmd_sessions_help
elsif args[0].to_s == client.name.to_s
print_status("Session #{client.name} is already interactive.")
else
print_status("Backgrounding session #{client.name}...")
# store the next session id so that it can be referenced as soon
# as this session is no longer interacting
client.next_session = args[0]
client.interacting = false
end
end

def cmd_resource_help
print_line 'Usage: resource path1 [path2 ...]'
print_line
print_line 'Run the commands stored in the supplied files. (- for stdin, press CTRL+D to end input from stdin)'
print_line 'Resource files may also contain ERB or Ruby code between <ruby></ruby> tags.'
print_line
end

def cmd_resource(*args)
if args.empty? || args.include?('-h') || args.include?('--help')
cmd_resource_help
return false
end

args.each do |res|
good_res = nil
if res == '-'
good_res = res
elsif ::File.exist?(res)
good_res = res
elsif [
::Msf::Config.script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter',
::Msf::Config.user_script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter'
].each do |dir|
res_path = dir + ::File::SEPARATOR + res
if ::File.exist?(res_path)
good_res = res_path
break
end
end
# let's check to see if it's in the scripts/resource dir (like when tab completed)
end
unless good_res
print_error("#{res} is not a valid resource file")
next
end

client.console.load_resource(good_res)
end
end

def cmd_resource_tabs(str, words)
tabs = []
# return tabs if words.length > 1
if (str && str =~ (/^#{Regexp.escape(::File::SEPARATOR)}/))
# then you are probably specifying a full path so let's just use normal file completion
return tab_complete_filenames(str, words)
elsif (!(words[1]) || !words[1].match(%r{^/}))
Comment on lines +207 to +208
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return tab_complete_filenames(str, words)
elsif (!(words[1]) || !words[1].match(%r{^/}))
return tab_complete_filenames(str, words)
end
if (!(words[1]) || !words[1].match(%r{^/}))

# then let's start tab completion in the scripts/resource directories
begin
[
::Msf::Config.script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter',
::Msf::Config.user_script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter',
'.'
].each do |dir|
next if !::File.exist? dir

tabs += ::Dir.new(dir).find_all do |e|
path = dir + ::File::SEPARATOR + e
::File.file?(path) and ::File.readable?(path)
end
end
rescue StandardError => e
elog('Problem tab completing resource file names in the scripts/resource directories', error: e)
end
else
tabs += tab_complete_filenames(str, words)
end

return tabs
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/rex/post/meterpreter/ui/console/command_dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Ui
###
module Console::CommandDispatcher

include Rex::Ui::Text::DispatcherShell::CommandDispatcher
include Msf::Ui::Console::CommandDispatcher::Session

#
# The hash of file names to class names after a module has already been
Expand Down
Loading