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
#
self.console = Rex::Post::Meterpreter::Ui::Console.new
self.console = Rex::Post::Meterpreter::Ui::Console.new(self)
end
##
@ -55,7 +55,7 @@ class Meterpreter < Rex::Post::Meterpreter::Client
# Initializes the console's I/O handles
#
def init_ui(input, output)
console.init_ui(user_input, user_output)
console.init_ui(input, output)
end
#
@ -74,6 +74,10 @@ class Meterpreter < Rex::Post::Meterpreter::Client
# interacting. This will allow the shell to abort if interaction is
# canceled.
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
protected

View File

@ -92,25 +92,11 @@ module Handler
# Handles an established connection supplied in the in and out
# handles. The handles are passed as parameters in case this
# handler is capable of handling multiple simultaneous
# connections. The default implementation simply creates a
# session using the payload's session factory reference and
# the supplied stream.
# connections. The default behavior is to attempt to create a session for
# the payload. This path will not be taken for mutli-staged payloads.
#
def handle_connection(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
create_session(conn)
end
#
@ -138,6 +124,28 @@ module Handler
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
# new session.

View File

@ -53,8 +53,18 @@ module Msf::Payload::Stager
print_status("Sending stage (#{p.length} bytes)")
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
# Aliases

View File

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

View File

@ -271,7 +271,7 @@ class Core
print_status("Starting interaction with #{session.name}...\n") if (quiet == false)
# 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
session.interact()

View File

@ -3,7 +3,6 @@ require 'msf/base'
require 'msf/ui'
require 'msf/ui/console/framework_event_manager'
require 'msf/ui/console/command_dispatcher'
require 'msf/ui/console/shell'
require 'msf/ui/console/table'
require 'find'
@ -32,12 +31,14 @@ class Driver < Msf::Ui::Driver
#
# The console driver is a command shell.
#
include Msf::Ui::Console::Shell
include Rex::Ui::Text::DispatcherShell
def initialize(prompt = "%umsf", prompt_char = ">%c")
# Call the parent
super
# Initialize attributes
self.framework = Msf::Simple::Framework.create
self.dispatcher_stack = []
self.framework = Msf::Simple::Framework.create
# Initialize config
Msf::Config.init
@ -46,9 +47,6 @@ class Driver < Msf::Ui::Driver
# stack
enstack_dispatcher(CommandDispatcher::Core)
# Initialize the super
super
# Register event handlers
register_event_handlers
@ -130,80 +128,12 @@ class Driver < Msf::Ui::Driver
run_single("banner")
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}: #{$!}\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_reader :framework
attr_accessor :active_module
protected
attr_writer :dispatcher_stack, :framework
attr_writer :framework
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
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/ui/console'

View File

@ -3,6 +3,8 @@ require 'rex/post/meterpreter'
module Rex
module Post
module Meterpreter
module Ui
###
#
@ -12,28 +14,26 @@ module Post
# This class provides a shell driven interface to the meterpreter client API.
#
###
class Meterpreter::Console
class Console
def initialize
# Initialize the pseudo-shell
shell = Rex::Ui::Text::Shell.new("%bmeterpreter%c ")
include Rex::Ui::Text::DispatcherShell
# 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
reset_ui
end
#
# 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
enstack_dispatcher(Console::Core)
end
#
@ -41,9 +41,9 @@ class Meterpreter::Console
# assumed that init_ui has been called prior.
#
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 (block)
@ -54,7 +54,15 @@ class Meterpreter::Console
}
end
attr_reader :client
protected
attr_writer :client
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
require 'rex/ui/text/input'
require 'rex/ui/text/shell'
require 'rex/ui/text/dispatcher_shell'
require 'rex/ui/text/color'
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,
# break out of the shell loop.
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
# process the line.
else
@ -115,6 +115,13 @@ module Shell
self.stop_flag = true
end
#
# Checks to see if the shell has stopped
#
def stopped?
self.stop_flag
end
#
# Change the input prompt
#

View File

@ -210,10 +210,7 @@ module DllInject
# Transmits the DLL injection payload and its associated DLL to the remote
# computer so that it can be loaded into memory.
#
def handle_connection(conn)
# Call the parent so that the stage gets sent
super
def handle_connection_stage(conn)
data = library_name + "\x00"
begin
@ -226,6 +223,10 @@ module DllInject
return
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)...")
# Send the size of the thing we're transferring
@ -234,6 +235,9 @@ module DllInject
conn.put(data)
print_status("Upload completed.")
# Call the parent so the session gets created.
super
end
end