more meterp action

git-svn-id: file:///home/svn/incoming/trunk@2791 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Matt Miller 2005-07-18 05:13:21 +00:00
parent 7f8db62b9c
commit 4679586c49
14 changed files with 335 additions and 175 deletions

View File

@ -28,7 +28,7 @@ class Meterpreter < Rex::Post::Meterpreter::Client
# #
# Create the console instance # Create the console instance
# #
self.console = Rex::Post::Meterpreter::Ui::Console.new self.console = Rex::Post::Meterpreter::Ui::Console.new(self)
end end
## ##
@ -55,7 +55,7 @@ class Meterpreter < Rex::Post::Meterpreter::Client
# Initializes the console's I/O handles # Initializes the console's I/O handles
# #
def init_ui(input, output) def init_ui(input, output)
console.init_ui(user_input, user_output) console.init_ui(input, output)
end end
# #
@ -74,6 +74,10 @@ class Meterpreter < Rex::Post::Meterpreter::Client
# interacting. This will allow the shell to abort if interaction is # interacting. This will allow the shell to abort if interaction is
# canceled. # canceled.
console.interact { self.interacting } console.interact { self.interacting }
# If the stop flag has been set, then that means the user exited. Raise
# the EOFError so we can drop this bitch like a bad habit.
raise EOFError if (console.stopped? == true)
end end
protected protected

View File

@ -92,25 +92,11 @@ module Handler
# Handles an established connection supplied in the in and out # Handles an established connection supplied in the in and out
# handles. The handles are passed as parameters in case this # handles. The handles are passed as parameters in case this
# handler is capable of handling multiple simultaneous # handler is capable of handling multiple simultaneous
# connections. The default implementation simply creates a # connections. The default behavior is to attempt to create a session for
# session using the payload's session factory reference and # the payload. This path will not be taken for mutli-staged payloads.
# the supplied stream.
# #
def handle_connection(conn) def handle_connection(conn)
# If the payload we merged in with has an associated session factory, create_session(conn)
# allocate a new session.
if (self.session)
s = self.session.new(conn)
# Pass along the framework context
s.framework = framework
# If the session is valid, register it with the framework and
# notify any waiters we may have.
if (s)
register_session(s)
end
end
end end
# #
@ -138,6 +124,28 @@ module Handler
protected protected
#
# Creates a session, if necessary, for the connection that's been handled.
# Sessions are only created if the payload that's been mixed in has an
# associated session.
#
def create_session(conn)
# If the payload we merged in with has an associated session factory,
# allocate a new session.
if (self.session)
s = self.session.new(conn)
# Pass along the framework context
s.framework = framework
# If the session is valid, register it with the framework and
# notify any waiters we may have.
if (s)
register_session(s)
end
end
end
# #
# Registers a session with the framework and notifies any waiters of the # Registers a session with the framework and notifies any waiters of the
# new session. # new session.

View File

@ -53,8 +53,18 @@ module Msf::Payload::Stager
print_status("Sending stage (#{p.length} bytes)") print_status("Sending stage (#{p.length} bytes)")
conn.put(p) conn.put(p)
super # Give the stages a chance to handle the connection
handle_connection_stage(conn)
end
#
# Called by handle_connection to allow the stage to process
# whatever it is it needs to process. The default is to simply attempt to
# create a session.
#
def handle_connection_stage(conn)
create_session(conn)
end end
# Aliases # Aliases

View File

@ -4,29 +4,12 @@ module Console
module CommandDispatcher module CommandDispatcher
def initialize(in_driver) include Rex::Ui::Text::DispatcherShell::CommandDispatcher
self.driver = in_driver
self.tab_complete_items = []
end
def print_error(msg = '') def initialize(driver)
driver.print_error(msg) super
end
def print_status(msg = '') self.driver = driver
driver.print_status(msg)
end
def print_line(msg = '')
driver.print_line(msg)
end
def print(msg = '')
driver.print(msg)
end
def update_prompt(prompt)
driver.update_prompt(prompt)
end end
def framework def framework
@ -48,13 +31,6 @@ module CommandDispatcher
dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_1) dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_1)
end end
#
# No tab completion items by default
#
attr_accessor :tab_complete_items
protected
attr_accessor :driver attr_accessor :driver
end end

View File

@ -271,7 +271,7 @@ class Core
print_status("Starting interaction with #{session.name}...\n") if (quiet == false) print_status("Starting interaction with #{session.name}...\n") if (quiet == false)
# Set the session's input and output handles # Set the session's input and output handles
session.init_ui(driver.input.dup, driver.output.dup) session.init_ui(driver.input, driver.output)
# Interact # Interact
session.interact() session.interact()

View File

@ -3,7 +3,6 @@ require 'msf/base'
require 'msf/ui' require 'msf/ui'
require 'msf/ui/console/framework_event_manager' require 'msf/ui/console/framework_event_manager'
require 'msf/ui/console/command_dispatcher' require 'msf/ui/console/command_dispatcher'
require 'msf/ui/console/shell'
require 'msf/ui/console/table' require 'msf/ui/console/table'
require 'find' require 'find'
@ -32,12 +31,14 @@ class Driver < Msf::Ui::Driver
# #
# The console driver is a command shell. # The console driver is a command shell.
# #
include Msf::Ui::Console::Shell include Rex::Ui::Text::DispatcherShell
def initialize(prompt = "%umsf", prompt_char = ">%c") def initialize(prompt = "%umsf", prompt_char = ">%c")
# Call the parent
super
# Initialize attributes # Initialize attributes
self.framework = Msf::Simple::Framework.create self.framework = Msf::Simple::Framework.create
self.dispatcher_stack = []
# Initialize config # Initialize config
Msf::Config.init Msf::Config.init
@ -46,9 +47,6 @@ class Driver < Msf::Ui::Driver
# stack # stack
enstack_dispatcher(CommandDispatcher::Core) enstack_dispatcher(CommandDispatcher::Core)
# Initialize the super
super
# Register event handlers # Register event handlers
register_event_handlers register_event_handlers
@ -130,80 +128,12 @@ class Driver < Msf::Ui::Driver
run_single("banner") run_single("banner")
end end
# attr_reader :framework
# Performs tab completion on shell input if supported
#
def tab_complete(str)
items = []
# Next, try to match internal command or value completion
# Enumerate each entry in the dispatcher stack
dispatcher_stack.each { |dispatcher|
# If it supports commands, query them all
if (dispatcher.respond_to?('commands'))
items.concat(dispatcher.commands.to_a.map { |x| x[0] })
end
# If the dispatcher has custom tab completion items, use them
items.concat(dispatcher.tab_complete_items || [])
}
items.find_all { |e|
e =~ /^#{str}/
}
end
# Run a single command line
def run_single(line)
arguments = parse_line(line)
method = arguments.shift
found = false
reset_color if (supports_color?)
if (method)
entries = dispatcher_stack.length
dispatcher_stack.each { |dispatcher|
begin
if (dispatcher.respond_to?('cmd_' + method))
eval("
dispatcher.#{'cmd_' + method}(*arguments)
found = true")
end
rescue
output.print_error("Error while running command #{method}: #{$!}\n#{$@.join("\n")}\n.")
end
# If the dispatcher stack changed as a result of this command,
# break out
break if (dispatcher_stack.length != entries)
}
if (!found)
output.print_error("Unknown command: #{method}.")
end
end
return found
end
# Push a dispatcher to the front of the stack
def enstack_dispatcher(dispatcher)
self.dispatcher_stack.unshift(dispatcher.new(self))
end
# Pop a dispatcher from the front of the stacker
def destack_dispatcher
self.dispatcher_stack.shift
end
attr_reader :dispatcher_stack, :framework
attr_accessor :active_module attr_accessor :active_module
protected protected
attr_writer :dispatcher_stack, :framework attr_writer :framework
end end

View File

@ -1,23 +0,0 @@
require 'msf/ui'
module Msf
module Ui
module Console
###
#
# Shell
# -----
#
# The shell class provides a command-prompt style interface in a
# generic fashion. This wrapper is just here in case we want to do custom
# shell extensions that don't make sense to throw in the rex shell.
#
###
module Shell
include Rex::Ui::Text::Shell
end
end end end

View File

@ -1,3 +1,4 @@
#!/usr/bin/ruby #!/usr/bin/ruby
require 'rex/post/meterpreter/client' require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/ui/console'

View File

@ -3,6 +3,8 @@ require 'rex/post/meterpreter'
module Rex module Rex
module Post module Post
module Meterpreter
module Ui
### ###
# #
@ -12,28 +14,26 @@ module Post
# This class provides a shell driven interface to the meterpreter client API. # This class provides a shell driven interface to the meterpreter client API.
# #
### ###
class Meterpreter::Console class Console
def initialize include Rex::Ui::Text::DispatcherShell
# Initialize the pseudo-shell
shell = Rex::Ui::Text::Shell.new("%bmeterpreter%c ") # Dispatchers
require 'rex/post/meterpreter/ui/console/core'
#
# Initialize the meterpreter console
#
def initialize(client)
super("%bmeterpreter%c")
# The meterpreter client context
self.client = client
# Point the input/output handles elsewhere # Point the input/output handles elsewhere
reset_ui reset_ui
end
# enstack_dispatcher(Console::Core)
# Initialize's the shells I/O handles
#
def init_ui(input, output)
shell.init_ui(input, output)
end
#
# Resets the shell's I/O handles
#
def reset_ui
shell.reset_ui
end end
# #
@ -41,9 +41,9 @@ class Meterpreter::Console
# assumed that init_ui has been called prior. # assumed that init_ui has been called prior.
# #
def interact(&block) def interact(&block)
shell.run { |line, args| run { |line|
# Run the command
# run_single(line)
# If a block was supplied, call it, otherwise return false # If a block was supplied, call it, otherwise return false
if (block) if (block)
@ -54,7 +54,15 @@ class Meterpreter::Console
} }
end end
attr_reader :client
protected
attr_writer :client
end end
end end
end end
end
end

View File

@ -0,0 +1,70 @@
require 'rex/parser/arguments'
module Rex
module Post
module Meterpreter
module Ui
###
#
# Core
# ----
#
# Core meterpreter client commands.
#
###
class Console::Core
include Rex::Ui::Text::DispatcherShell::CommandDispatcher
@@use_opts = Rex::Parser::Arguments.new(
"-m" => [ true, "The name of the module or modules to load (Ex: stdapi)." ],
"-h" => [ false, "Help banner." ])
#
# List of supported commands
#
def commands
{
"exit" => "Terminate the meterpreter session",
"use" => "Load a one or more meterpreter extensions",
"quit" => "Terminate the meterpreter session",
}
end
#
# Terminates the meterpreter session
#
def cmd_exit(*args)
shell.stop
end
alias cmd_quit cmd_exit
#
# Loads one or more meterpreter extensions
#
def cmd_use(*args)
if (args.length == 0)
args.unshift("-h")
end
@@use_opts.parse(args) { |opt, idx, val|
case opt
when "-m"
mod = val
when "-h"
print(
"Usage: use [options]\n\n" +
"Loads a meterpreter extension module or modules.\n" +
@use_opts.usage)
end
}
end
end
end
end
end
end

View File

@ -10,6 +10,7 @@ require 'rex/ui/progress_tracker'
# Text-based user interfaces # Text-based user interfaces
require 'rex/ui/text/input' require 'rex/ui/text/input'
require 'rex/ui/text/shell' require 'rex/ui/text/shell'
require 'rex/ui/text/dispatcher_shell'
require 'rex/ui/text/color' require 'rex/ui/text/color'
require 'rex/ui/text/table' require 'rex/ui/text/table'

View File

@ -0,0 +1,164 @@
require 'rex/ui'
module Rex
module Ui
module Text
###
#
# DispatcherShell
# ---------------
#
# The dispatcher shell class is designed to provide a generic means
# of processing various shell commands that may be located in
# different modules or chunks of codes. These chunks are referred
# to as command dispatchers. The only requirement for command dispatchers is
# that they prefix every method that they wish to be mirrored as a command
# with the cmd_ prefix.
#
###
module DispatcherShell
###
#
# CommandDispatcher
# -----------------
#
# Empty template base class for command dispatchers
#
###
module CommandDispatcher
def initialize(shell)
self.shell = shell
self.tab_complete_items = []
end
def commands
end
def print_error(msg = '')
shell.print_error(msg)
end
def print_status(msg = '')
shell.print_status(msg)
end
def print_line(msg = '')
shell.print_line(msg)
end
def print(msg = '')
shell.print(msg)
end
def update_prompt(prompt)
shell.update_prompt(prompt)
end
#
# No tab completion items by default
#
attr_accessor :shell, :tab_complete_items
end
#
# DispatcherShell derives from shell
#
include Shell
#
# Initialize the dispatcher shell
#
def initialize(prompt, prompt_char = '>')
super
self.dispatcher_stack = []
end
#
# Performs tab completion on shell input if supported
#
def tab_complete(str)
items = []
# Next, try to match internal command or value completion
# Enumerate each entry in the dispatcher stack
dispatcher_stack.each { |dispatcher|
# If it supports commands, query them all
if (dispatcher.respond_to?('commands'))
items.concat(dispatcher.commands.to_a.map { |x| x[0] })
end
# If the dispatcher has custom tab completion items, use them
items.concat(dispatcher.tab_complete_items || [])
}
items.find_all { |e|
e =~ /^#{str}/
}
end
# Run a single command line
def run_single(line)
arguments = parse_line(line)
method = arguments.shift
found = false
reset_color if (supports_color?)
if (method)
entries = dispatcher_stack.length
dispatcher_stack.each { |dispatcher|
begin
if (dispatcher.respond_to?('cmd_' + method))
eval("
dispatcher.#{'cmd_' + method}(*arguments)
found = true")
end
rescue
output.print_error("Error while running command #{method}: #{$!}")
end
# If the dispatcher stack changed as a result of this command,
# break out
break if (dispatcher_stack.length != entries)
}
if (found == false)
unknown_command(method, line)
end
end
return found
end
#
# If the command is unknown...
#
def unknown_command(method, line)
output.print_error("Unknown command: #{method}.")
end
# Push a dispatcher to the front of the stack
def enstack_dispatcher(dispatcher)
self.dispatcher_stack.unshift(dispatcher.new(self))
end
# Pop a dispatcher from the front of the stacker
def destack_dispatcher
self.dispatcher_stack.shift
end
attr_accessor :dispatcher_stack
end
end
end
end

View File

@ -96,7 +96,7 @@ module Shell
# If a block was passed in, pass the line to it. If it returns true, # If a block was passed in, pass the line to it. If it returns true,
# break out of the shell loop. # break out of the shell loop.
if (block) if (block)
break if (block.call(line, parse_line(line))) break if (block.call(line))
# Otherwise, call what should be an overriden instance method to # Otherwise, call what should be an overriden instance method to
# process the line. # process the line.
else else
@ -115,6 +115,13 @@ module Shell
self.stop_flag = true self.stop_flag = true
end end
#
# Checks to see if the shell has stopped
#
def stopped?
self.stop_flag
end
# #
# Change the input prompt # Change the input prompt
# #

View File

@ -210,10 +210,7 @@ module DllInject
# Transmits the DLL injection payload and its associated DLL to the remote # Transmits the DLL injection payload and its associated DLL to the remote
# computer so that it can be loaded into memory. # computer so that it can be loaded into memory.
# #
def handle_connection(conn) def handle_connection_stage(conn)
# Call the parent so that the stage gets sent
super
data = library_name + "\x00" data = library_name + "\x00"
begin begin
@ -226,6 +223,10 @@ module DllInject
return return
end end
# Give the stage a second or so, just so it doesn't try
# to read in the DLL as part of the stage...
Rex::ThreadSafe.sleep(1.5)
print_status("Uploading DLL (#{data.length} bytes)...") print_status("Uploading DLL (#{data.length} bytes)...")
# Send the size of the thing we're transferring # Send the size of the thing we're transferring
@ -234,6 +235,9 @@ module DllInject
conn.put(data) conn.put(data)
print_status("Upload completed.") print_status("Upload completed.")
# Call the parent so the session gets created.
super
end end
end end