# -*- coding: binary -*- require 'set' require 'rex/post/meterpreter' require 'rex/parser/arguments' module Rex module Post module Meterpreter module Ui ### # # Core meterpreter client commands that provide only the required set of # commands for having a functional meterpreter client<->server instance. # ### class Console::CommandDispatcher::Core include Console::CommandDispatcher # # Initializes an instance of the core command set using the supplied shell # for interactivity. # def initialize(shell) super self.extensions = [] self.bgjobs = [] self.bgjob_id = 0 # keep a lookup table to refer to transports by index @transport_map = {} end @@irb_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner." ], "-e" => [ true, "Expression to evaluate." ]) @@load_opts = Rex::Parser::Arguments.new( "-l" => [ false, "List all available extensions" ], "-h" => [ false, "Help menu." ]) # # List of supported commands. # def commands c = { "?" => "Help menu", "background" => "Backgrounds the current session", "close" => "Closes a channel", "channel" => "Displays information or control active channels", "exit" => "Terminate the meterpreter session", "help" => "Help menu", "irb" => "Drop into irb scripting mode", "use" => "Deprecated alias for 'load'", "load" => "Load one or more meterpreter extensions", "machine_id" => "Get the MSF ID of the machine attached to the session", "quit" => "Terminate the meterpreter session", "resource" => "Run the commands stored in a file", "uuid" => "Get the UUID for the current session", "read" => "Reads data from a channel", "run" => "Executes a meterpreter script or Post module", "bgrun" => "Executes a meterpreter script as a background thread", "bgkill" => "Kills a background meterpreter script", "get_timeouts" => "Get the current session timeout values", "set_timeouts" => "Set the current session timeout values", "bglist" => "Lists running background scripts", "write" => "Writes data to a channel", "enable_unicode_encoding" => "Enables encoding of unicode strings", "disable_unicode_encoding" => "Disables encoding of unicode strings" } if client.passive_service c["detach"] = "Detach the meterpreter session (for http/https)" end # Currently we have some windows-specific core commands` if client.platform =~ /win/ # only support the SSL switching for HTTPS if client.passive_service && client.sock.type? == 'tcp-ssl' c["ssl_verify"] = "Modify the SSL certificate verification setting" end end if client.platform =~ /win/ || client.platform =~ /linux/ # Migration only supported on windows and linux c["migrate"] = "Migrate the server to another process" end if client.platform =~ /win/ || client.platform =~ /linux/ || client.platform =~ /python/ || client.platform =~ /java/ # Yet to implement transport hopping for other meterpreters. c["transport"] = "Change the current transport mechanism" # sleep functionality relies on the transport features, so only # wire that in with the transport stuff. c["sleep"] = "Force Meterpreter to go quiet, then re-establish session." end if (msf_loaded?) c["info"] = "Displays information about a Post module" end c end # # Core baby. # def name "Core" 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 print_status "Backgrounding session #{client.name}..." client.interacting = false end # # Displays information about active channels # @@channel_opts = Rex::Parser::Arguments.new( "-c" => [ true, "Close the given channel." ], "-k" => [ true, "Close the given channel." ], "-i" => [ true, "Interact with the given channel." ], "-l" => [ false, "List active channels." ], "-r" => [ true, "Read from the given channel." ], "-w" => [ true, "Write to the given channel." ], "-h" => [ false, "Help menu." ]) def cmd_channel_help print_line "Usage: channel [options]" print_line print_line "Displays information about active channels." print_line @@channel_opts.usage end # # Performs operations on the supplied channel. # def cmd_channel(*args) if args.empty? or args.include?("-h") or args.include?("--help") cmd_channel_help return end mode = nil chan = nil # Parse options @@channel_opts.parse(args) { |opt, idx, val| case opt when "-l" mode = :list when "-c", "-k" mode = :close chan = val when "-i" mode = :interact chan = val when "-r" mode = :read chan = val when "-w" mode = :write chan = val end if @@channel_opts.arg_required?(opt) unless chan print_error("Channel ID required") return end end } case mode when :list tbl = Rex::Ui::Text::Table.new( 'Indent' => 4, 'Columns' => [ 'Id', 'Class', 'Type' ]) items = 0 client.channels.each_pair { |cid, channel| tbl << [ cid, channel.class.cls, channel.type ] items += 1 } if (items == 0) print_line("No active channels.") else print("\n" + tbl.to_s + "\n") end when :close cmd_close(chan) when :interact cmd_interact(chan) when :read cmd_read(chan) when :write cmd_write(chan) else # No mode, no service. return true end end def cmd_channel_tabs(str, words) case words.length when 1 @@channel_opts.fmt.keys when 2 case words[1] when "-k", "-c", "-i", "-r", "-w" tab_complete_channels else [] end else [] end end def cmd_close_help print_line "Usage: close " print_line print_line "Closes the supplied channel." print_line end # # Closes a supplied channel. # def cmd_close(*args) if (args.length == 0) cmd_close_help return true end cid = args[0].to_i channel = client.find_channel(cid) if (!channel) print_error("Invalid channel identifier specified.") return true else channel._close # Issue #410 print_status("Closed channel #{cid}.") end end def cmd_close_tabs(str, words) return [] if words.length > 1 return tab_complete_channels end # # Terminates the meterpreter session. # def cmd_exit(*args) print_status("Shutting down Meterpreter...") client.core.shutdown rescue nil client.shutdown_passive_dispatcher shell.stop end alias cmd_quit cmd_exit def cmd_detach_help print_line "Detach from the victim. Only possible for non-stream sessions (http/https)" print_line print_line "The victim will continue to attempt to call back to the handler until it" print_line "successfully connects (which may happen immediately if you have a handler" print_line "running in the background), or reaches its expiration." print_line print_line "This session may #{client.passive_service ? "" : "NOT"} be detached." print_line end # # Disconnects the session # def cmd_detach(*args) client.shutdown_passive_dispatcher shell.stop end def cmd_interact_help print_line "Usage: interact " print_line print_line "Interacts with the supplied channel." print_line end # # Interacts with a channel. # def cmd_interact(*args) if (args.length == 0) cmd_info_help return true end cid = args[0].to_i channel = client.find_channel(cid) if (channel) print_line("Interacting with channel #{cid}...\n") shell.interact_with_channel(channel) else print_error("Invalid channel identifier specified.") end end alias cmd_interact_tabs cmd_close_tabs def cmd_irb_help print_line "Usage: irb" print_line print_line "Execute commands in a Ruby environment" print @@irb_opts.usage end # # Runs the IRB scripting shell # 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 = client framework = client.framework if expressions.empty? print_status("Starting IRB shell") print_status("The 'client' variable holds the meterpreter client\n") Rex::Ui::Text::IrbShell.new(binding).run else expressions.each { |expression| eval(expression, binding) } end end @@set_timeouts_opts = Rex::Parser::Arguments.new( '-c' => [ true, 'Comms timeout (seconds)' ], '-x' => [ true, 'Expiration timout (seconds)' ], '-t' => [ true, 'Retry total time (seconds)' ], '-w' => [ true, 'Retry wait time (seconds)' ], '-h' => [ false, 'Help menu' ]) def cmd_set_timeouts_help print_line('Usage: set_timeouts [options]') print_line print_line('Set the current timeout options.') print_line('Any or all of these can be set at once.') print_line(@@set_timeouts_opts.usage) end def cmd_set_timeouts(*args) if ( args.length == 0 or args.include?("-h") ) cmd_set_timeouts_help return end opts = {} @@set_timeouts_opts.parse(args) do |opt, idx, val| case opt when '-c' opts[:comm_timeout] = val.to_i if val when '-x' opts[:session_exp] = val.to_i if val when '-t' opts[:retry_total] = val.to_i if val when '-w' opts[:retry_wait] = val.to_i if val end end if opts.keys.length == 0 print_error("No options set") else timeouts = client.core.set_transport_timeouts(opts) print_timeouts(timeouts) end end def cmd_get_timeouts(*args) # Calling set without passing values is the same as # getting all the current timeouts timeouts = client.core.set_transport_timeouts print_timeouts(timeouts) end def print_timeouts(timeouts) if timeouts[:session_exp] print_line("Session Expiry : @ #{(Time.now + timeouts[:session_exp]).strftime('%Y-%m-%d %H:%M:%S')}") end if timeouts[:comm_timeout] print_line("Comm Timeout : #{timeouts[:comm_timeout]} seconds") end if timeouts[:retry_total] print_line("Retry Total Time: #{timeouts[:retry_total]} seconds") end if timeouts[:retry_wait] print_line("Retry Wait Time : #{timeouts[:retry_wait]} seconds") end end # # Get the machine ID of the target # def cmd_machine_id(*args) client.machine_id = client.core.machine_id unless client.machine_id print_good("Machine ID: #{client.machine_id}") end # # Get the machine ID of the target # def cmd_uuid(*args) client.payload_uuid = client.core.uuid unless client.payload_uuid print_good("UUID: #{client.payload_uuid}") end # # Arguments for ssl verification # @@ssl_verify_opts = Rex::Parser::Arguments.new( '-e' => [ false, 'Enable SSL certificate verification' ], '-d' => [ false, 'Disable SSL certificate verification' ], '-q' => [ false, 'Query the statis of SSL certificate verification' ], '-h' => [ false, 'Help menu' ]) # # Help for ssl verification # def cmd_ssl_verify_help print_line('Usage: ssl_verify [options]') print_line print_line('Change and query the current setting for SSL verification') print_line('Only one of the following options can be used at a time') print_line(@@ssl_verify_opts.usage) end # # Handle the SSL verification querying and setting function. # def cmd_ssl_verify(*args) if ( args.length == 0 or args.include?("-h") ) cmd_ssl_verify_help return end query = false enable = false disable = false settings = 0 @@ssl_verify_opts.parse(args) do |opt, idx, val| case opt when '-q' query = true settings += 1 when '-e' enable = true settings += 1 when '-d' disable = true settings += 1 end end # Make sure only one action has been chosen if settings != 1 cmd_ssl_verify_help return end if query hash = client.core.get_ssl_hash_verify if hash print_good("SSL verification is enabled. SHA1 Hash: #{hash.unpack("H*")[0]}") else print_good("SSL verification is disabled.") end elsif enable hash = client.core.enable_ssl_hash_verify if hash print_good("SSL verification has been enabled. SHA1 Hash: #{hash.unpack("H*")[0]}") else print_error("Failed to enable SSL verification") end else if client.core.disable_ssl_hash_verify print_good('SSL verification has been disabled') else print_error("Failed to disable SSL verification") end end end # # Display help for the sleep. # def cmd_sleep_help print_line('Usage: sleep