# -*- coding: binary -*- 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 end @@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 about active channels", "exit" => "Terminate the meterpreter session", "help" => "Help menu", "interact" => "Interacts with a channel", "irb" => "Drop into irb scripting mode", "use" => "Deprecated alias for 'load'", "load" => "Load one or more meterpreter extensions", "quit" => "Terminate the meterpreter session", "resource" => "Run the commands stored in a file", "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", "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 # The only meterp that implements this right now is native Windows and for # whatever reason it is not adding core_migrate to its list of commands. # Use a dumb platform til it gets sorted. #if client.commands.include? "core_migrate" if client.platform =~ /win/ c["migrate"] = "Migrate the server to another process" 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 data = [] # 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) if not client.passive_service print_error("Detach is only possible for non-stream sessions (http/https)") return end 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 # # Runs the IRB scripting shell # def cmd_irb(*args) print_status("Starting IRB shell") print_status("The 'client' variable holds the meterpreter client\n") Rex::Ui::Text::IrbShell.new(binding).run end def cmd_migrate_help print_line "Usage: migrate " print_line print_line "Migrates the server instance to another process." print_line "NOTE: Any open channels or other dynamic state will be lost." print_line end # # Migrates the server to the supplied process identifier. # def cmd_migrate(*args) if (args.length == 0) cmd_migrate_help return true end pid = args[0].to_i if(pid == 0) print_error("A process ID must be specified, not a process name") return end print_status("Migrating to #{pid}...") # Do this thang. client.core.migrate(pid) print_status("Migration completed successfully.") end def cmd_load_help print_line("Usage: load ext1 ext2 ext3 ...") print_line print_line "Loads a meterpreter extension module or modules." print_line @@load_opts.usage end # # Loads one or more meterpreter extensions. # def cmd_load(*args) if (args.length == 0) args.unshift("-h") end modules = nil @@load_opts.parse(args) { |opt, idx, val| case opt when "-l" exts = [] path = ::File.join(Msf::Config.install_root, 'data', 'meterpreter') ::Dir.entries(path).each { |f| if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ ) exts.push($1) end } print(exts.sort.join("\n") + "\n") return true when "-h" cmd_load_help return true end } # Load each of the modules args.each { |m| md = m.downcase if (extensions.include?(md)) print_error("The '#{md}' extension has already been loaded.") next end print("Loading extension #{md}...") begin # Use the remote side, then load the client-side if (client.core.use(md) == true) add_extension_client(md) end rescue print_line log_error("Failed to load extension: #{$!}") next end print_line("success.") } return true end def cmd_load_tabs(str, words) tabs = [] path = ::File.join(Msf::Config.install_root, 'data', 'meterpreter') ::Dir.entries(path).each { |f| if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ ) if (not extensions.include?($1)) tabs.push($1) end end } return tabs end def cmd_use(*args) #print_error("Warning: The 'use' command is deprecated in favor of 'load'") cmd_load(*args) end alias cmd_use_help cmd_load_help alias cmd_use_tabs cmd_load_tabs def cmd_read_help print_line "Usage: read [length]" print_line print_line "Reads data from the supplied channel." print_line end # # Reads data from a channel. # def cmd_read(*args) if (args.length == 0) cmd_read_help return true end cid = args[0].to_i length = (args.length >= 2) ? args[1].to_i : 16384 channel = client.find_channel(cid) if (!channel) print_error("Channel #{cid} is not valid.") return true end data = channel.read(length) if (data and data.length) print("Read #{data.length} bytes from #{cid}:\n\n#{data}\n") else print_error("No data was returned.") end return true end alias cmd_read_tabs cmd_close_tabs def cmd_run_help print_line "Usage: run