Skip to content

Commit e011fbe

Browse files
authored
Land #18516, extract common dispatcher commands into a single resuable mixin
Extract reusable core session commands
2 parents 04361e1 + ea41ec7 commit e011fbe

File tree

9 files changed

+592
-219
lines changed

9 files changed

+592
-219
lines changed

lib/msf/base/sessions/command_shell.rb

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class CommandShell
3030
include Rex::Ui::Text::Resource
3131

3232
@@irb_opts = Rex::Parser::Arguments.new(
33-
'-h' => [false, 'Help menu.' ],
33+
['-h', '--help'] => [false, 'Help menu.' ],
3434
'-e' => [true, 'Expression to evaluate.']
3535
)
3636

@@ -235,35 +235,35 @@ def cmd_sessions_help
235235
end
236236

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

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

248-
if args.length != 1
249-
# More than one argument
247+
session_id = args[0].to_i
248+
if session_id <= 0
249+
print_status 'Invalid session id'
250250
return cmd_sessions_help
251251
end
252252

253-
if args[0].to_s == self.name.to_s
253+
if session_id == self.sid
254254
# Src == Dst
255255
print_status("Session #{self.name} is already interactive.")
256256
else
257257
print_status("Backgrounding session #{self.name}...")
258258
# store the next session id so that it can be referenced as soon
259259
# as this session is no longer interacting
260-
self.next_session = args[0]
260+
self.next_session = session_id
261261
self.interacting = false
262262
end
263263
end
264264

265265
def cmd_resource(*args)
266-
if args.empty?
266+
if args.empty? || args[0] == '-h' || args[0] == '--help'
267267
cmd_resource_help
268268
return false
269269
end
@@ -320,9 +320,9 @@ def cmd_shell_help()
320320
end
321321

322322
def cmd_shell(*args)
323-
if args.length == 1 && (args[1] == '-h' || args[1] == 'help')
324-
# One arg, and args[1] => '-h' '-H' 'help'
325-
return cmd_sessions_help
323+
if args.length == 1 && (args[0] == '-h' || args[0] == '--help')
324+
# One arg, and args[0] => '-h' '--help'
325+
return cmd_shell_help
326326
end
327327

328328
if platform == 'windows'
@@ -570,7 +570,7 @@ def cmd_pry_help
570570
# Open the Pry debugger on the current session
571571
#
572572
def cmd_pry(*args)
573-
if args.include?('-h')
573+
if args.include?('-h') || args.include?('--help')
574574
cmd_pry_help
575575
return
576576
end

lib/msf/base/sessions/meterpreter.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ def initialize(rstream, opts={})
9595
self.console = Rex::Post::Meterpreter::Ui::Console.new(self)
9696
end
9797

98+
def exit
99+
begin
100+
self.core.shutdown
101+
rescue StandardError
102+
nil
103+
end
104+
self.shutdown_passive_dispatcher
105+
self.console.stop
106+
end
98107
#
99108
# Returns the session type as being 'meterpreter'.
100109
#
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# frozen_string_literal: true
2+
3+
module Msf
4+
module Ui
5+
module Console
6+
module CommandDispatcher
7+
module Session
8+
include Rex::Ui::Text::DispatcherShell::CommandDispatcher
9+
10+
@@irb_opts = Rex::Parser::Arguments.new(
11+
%w[-h --help] => [false, 'Help menu.' ],
12+
'-e' => [true, 'Expression to evaluate.']
13+
)
14+
def commands
15+
{
16+
'?' => 'Help menu',
17+
'background' => 'Backgrounds the current session',
18+
'bg' => 'Alias for background',
19+
'exit' => 'Terminate the session',
20+
'help' => 'Help menu',
21+
'irb' => 'Open an interactive Ruby shell on the current session',
22+
'pry' => 'Open the Pry debugger on the current session',
23+
'quit' => 'Terminate the session',
24+
'resource' => 'Run the commands stored in a file',
25+
'uuid' => 'Get the UUID for the current session',
26+
'sessions' => 'Quickly switch to another session'
27+
}
28+
end
29+
30+
def cmd_background_help
31+
print_line('Usage: background')
32+
print_line
33+
print_line('Stop interacting with this session and return to the parent prompt')
34+
print_line
35+
end
36+
37+
def cmd_background(*args)
38+
if args.include?('-h') || args.include?('--help')
39+
cmd_background_help
40+
return
41+
end
42+
print_status("Backgrounding session #{client.name}...")
43+
client.interacting = false
44+
end
45+
46+
alias cmd_bg cmd_background
47+
alias cmd_bg_help cmd_background_help
48+
49+
#
50+
# Terminates the session.
51+
#
52+
def cmd_exit(*args)
53+
print_status("Shutting down session: #{client.sid}")
54+
client.exit
55+
end
56+
57+
alias cmd_quit cmd_exit
58+
59+
def cmd_irb_help
60+
print_line('Usage: irb')
61+
print_line
62+
print_line('Open an interactive Ruby shell on the current session.')
63+
print @@irb_opts.usage
64+
end
65+
66+
def cmd_irb_tabs(str, words)
67+
return [] if words.length > 1
68+
69+
@@irb_opts.option_keys
70+
end
71+
72+
#
73+
# Open an interactive Ruby shell on the current session
74+
#
75+
def cmd_irb(*args)
76+
expressions = []
77+
78+
# Parse the command options
79+
@@irb_opts.parse(args) do |opt, _idx, val|
80+
case opt
81+
when '-e'
82+
expressions << val
83+
when '-h', '--help'
84+
return cmd_irb_help
85+
end
86+
end
87+
88+
session = client
89+
framework = client.framework
90+
91+
if expressions.empty?
92+
print_status('Starting IRB shell...')
93+
print_status("You are in the \"client\" (session) object\n")
94+
framework.history_manager.with_context(name: :irb) do
95+
Rex::Ui::Text::IrbShell.new(client).run
96+
end
97+
else
98+
# XXX: No vprint_status here
99+
if framework.datastore['VERBOSE'].to_s == 'true'
100+
print_status("You are executing expressions in #{binding.receiver}")
101+
end
102+
103+
expressions.each { |expression| eval(expression, binding) }
104+
end
105+
end
106+
107+
def cmd_pry_help
108+
print_line 'Usage: pry'
109+
print_line
110+
print_line 'Open the Pry debugger on the current session.'
111+
print_line
112+
end
113+
114+
#
115+
# Open the Pry debugger on the current session
116+
#
117+
def cmd_pry(*args)
118+
if args.include?('-h') || args.include?('--help')
119+
cmd_pry_help
120+
return
121+
end
122+
123+
begin
124+
require 'pry'
125+
rescue LoadError
126+
print_error('Failed to load Pry, try "gem install pry"')
127+
return
128+
end
129+
130+
print_status('Starting Pry shell...')
131+
print_status("You are in the \"client\" (session) object\n")
132+
133+
Pry.config.history_load = false
134+
client.framework.history_manager.with_context(history_file: Msf::Config.pry_history, name: :pry) do
135+
client.pry
136+
end
137+
end
138+
139+
def cmd_sessions_help
140+
print_line('Usage: sessions <id>')
141+
print_line
142+
print_line('Interact with a different session Id.')
143+
print_line('This works the same as calling this from the MSF shell: sessions -i <session id>')
144+
print_line
145+
end
146+
147+
def cmd_sessions(*args)
148+
if args.empty? || args[0].to_i == 0
149+
cmd_sessions_help
150+
elsif args[0].to_s == client.name.to_s
151+
print_status("Session #{client.name} is already interactive.")
152+
else
153+
print_status("Backgrounding session #{client.name}...")
154+
# store the next session id so that it can be referenced as soon
155+
# as this session is no longer interacting
156+
client.next_session = args[0]
157+
client.interacting = false
158+
end
159+
end
160+
161+
def cmd_resource_help
162+
print_line 'Usage: resource path1 [path2 ...]'
163+
print_line
164+
print_line 'Run the commands stored in the supplied files. (- for stdin, press CTRL+D to end input from stdin)'
165+
print_line 'Resource files may also contain ERB or Ruby code between <ruby></ruby> tags.'
166+
print_line
167+
end
168+
169+
def cmd_resource(*args)
170+
if args.empty? || args.include?('-h') || args.include?('--help')
171+
cmd_resource_help
172+
return false
173+
end
174+
175+
args.each do |res|
176+
good_res = nil
177+
if res == '-'
178+
good_res = res
179+
elsif ::File.exist?(res)
180+
good_res = res
181+
elsif [
182+
::Msf::Config.script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter',
183+
::Msf::Config.user_script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter'
184+
].each do |dir|
185+
res_path = dir + ::File::SEPARATOR + res
186+
if ::File.exist?(res_path)
187+
good_res = res_path
188+
break
189+
end
190+
end
191+
# let's check to see if it's in the scripts/resource dir (like when tab completed)
192+
end
193+
unless good_res
194+
print_error("#{res} is not a valid resource file")
195+
next
196+
end
197+
198+
client.console.load_resource(good_res)
199+
end
200+
end
201+
202+
def cmd_resource_tabs(str, words)
203+
tabs = []
204+
# return tabs if words.length > 1
205+
if (str && str =~ (/^#{Regexp.escape(::File::SEPARATOR)}/))
206+
# then you are probably specifying a full path so let's just use normal file completion
207+
return tab_complete_filenames(str, words)
208+
elsif (!(words[1]) || !words[1].match(%r{^/}))
209+
# then let's start tab completion in the scripts/resource directories
210+
begin
211+
[
212+
::Msf::Config.script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter',
213+
::Msf::Config.user_script_directory + ::File::SEPARATOR + 'resource' + ::File::SEPARATOR + 'meterpreter',
214+
'.'
215+
].each do |dir|
216+
next if !::File.exist? dir
217+
218+
tabs += ::Dir.new(dir).find_all do |e|
219+
path = dir + ::File::SEPARATOR + e
220+
::File.file?(path) and ::File.readable?(path)
221+
end
222+
end
223+
rescue StandardError => e
224+
elog('Problem tab completing resource file names in the scripts/resource directories', error: e)
225+
end
226+
else
227+
tabs += tab_complete_filenames(str, words)
228+
end
229+
230+
return tabs
231+
end
232+
end
233+
end
234+
end
235+
end
236+
end

lib/rex/post/meterpreter/ui/console/command_dispatcher.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module Ui
1313
###
1414
module Console::CommandDispatcher
1515

16-
include Rex::Ui::Text::DispatcherShell::CommandDispatcher
16+
include Msf::Ui::Console::CommandDispatcher::Session
1717

1818
#
1919
# The hash of file names to class names after a module has already been

0 commit comments

Comments
 (0)