moved some code around for interactive channels, still not functional yet, boohoo
git-svn-id: file:///home/svn/incoming/trunk@2797 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
203f185ad7
commit
632a97823f
|
@ -48,3 +48,5 @@ service.shutdown
|
|||
- findsock handler
|
||||
- meterpreter
|
||||
- more ui wrapping
|
||||
- fix route addition/removal in stdapi server dll (mib structure issue)
|
||||
- fix interactive stream pool channels
|
||||
|
|
|
@ -36,25 +36,7 @@ protected
|
|||
# overriden by derived classes if they wish to do this another way.
|
||||
#
|
||||
def _interact
|
||||
while self.interacting
|
||||
# Select input and rstream
|
||||
sd = Rex::ThreadSafe.select([ user_input.fd, rstream.fd ])
|
||||
|
||||
# Cycle through the items that have data
|
||||
# From the rstream? Write to user_output.
|
||||
sd[0].each { |s|
|
||||
if (s == rstream.fd)
|
||||
data = rstream.get
|
||||
|
||||
user_output.print(data)
|
||||
# From user_input? Write to rstream.
|
||||
elsif (s == user_input.fd)
|
||||
data = user_input.gets
|
||||
|
||||
rstream.put(data)
|
||||
end
|
||||
} if (sd)
|
||||
end
|
||||
interact_stream(rstream)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ module Interactive
|
|||
# Interactive sessions by default may interact with the local user input
|
||||
# and output.
|
||||
#
|
||||
include Rex::Ui::Subscriber
|
||||
include Rex::Ui::Interactive
|
||||
|
||||
#
|
||||
# Initialize's the session
|
||||
|
@ -55,75 +55,43 @@ module Interactive
|
|||
rstream = nil
|
||||
end
|
||||
|
||||
#
|
||||
# Starts interacting with the session at the most raw level, simply
|
||||
# forwarding input from user_input to rstream and forwarding input from
|
||||
# rstream to user_output.
|
||||
#
|
||||
def interact
|
||||
self.interacting = true
|
||||
|
||||
eof = false
|
||||
|
||||
# Handle suspend notifications
|
||||
handle_suspend
|
||||
|
||||
callcc { |ctx|
|
||||
# As long as we're interacting...
|
||||
while (self.interacting == true)
|
||||
begin
|
||||
_interact
|
||||
# If we get an interrupt exception, ask the user if they want to
|
||||
# abort the interaction. If they do, then we return out of
|
||||
# the interact function and call it a day.
|
||||
rescue Interrupt
|
||||
if (user_want_abort? == true)
|
||||
eof = true
|
||||
ctx.call
|
||||
end
|
||||
# If we reach EOF or the connection is reset...
|
||||
rescue EOFError, Errno::ECONNRESET
|
||||
dlog("Session #{name} got EOF, closing.", 'core', LEV_1)
|
||||
eof = true
|
||||
ctx.call
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
# Restore the suspend handler
|
||||
restore_suspend
|
||||
|
||||
# If we hit end-of-file, then that means we should finish off this
|
||||
# session and call it a day.
|
||||
framework.sessions.deregister(self) if (eof == true)
|
||||
|
||||
# Return whether or not EOF was reached
|
||||
return eof
|
||||
end
|
||||
|
||||
#
|
||||
# The remote stream handle. Must inherit from Rex::IO::Stream.
|
||||
#
|
||||
attr_accessor :rstream
|
||||
#
|
||||
# Whether or not the session is currently being interacted with
|
||||
#
|
||||
attr_reader :interacting
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :interacting
|
||||
#
|
||||
# The original suspend proc.
|
||||
#
|
||||
attr_accessor :orig_suspend
|
||||
|
||||
#
|
||||
# Stub method that is meant to handler interaction
|
||||
#
|
||||
def _interact
|
||||
end
|
||||
|
||||
#
|
||||
# Check to see if the user wants to abort
|
||||
#
|
||||
def _interrupt
|
||||
user_want_abort?
|
||||
end
|
||||
|
||||
#
|
||||
# Check to see if we should suspnd
|
||||
#
|
||||
def _suspend
|
||||
# Ask the user if they would like to background the session
|
||||
if (prompt_yesno("Background session #{name}?") == true)
|
||||
self.interacting = false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# If the session reaches EOF, deregister it.
|
||||
#
|
||||
def _interact_complete
|
||||
framework.sessions.deregister(self)
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if the user wants to abort
|
||||
#
|
||||
|
@ -131,49 +99,6 @@ protected
|
|||
prompt_yesno("Abort session #{name}?")
|
||||
end
|
||||
|
||||
#
|
||||
# Installs a signal handler to monitor suspend signal notifications.
|
||||
#
|
||||
def handle_suspend
|
||||
if (orig_suspend == nil)
|
||||
self.orig_suspend = Signal.trap("TSTP") {
|
||||
# Ask the user if they would like to background the session
|
||||
if (prompt_yesno("Background session #{name}?") == true)
|
||||
self.interacting = false
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Restores the previously installed signal handler for suspend
|
||||
# notifications.
|
||||
#
|
||||
def restore_suspend
|
||||
if (orig_suspend)
|
||||
Signal.trap("TSTP", orig_suspend)
|
||||
|
||||
self.orig_suspend = nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Prompt the user for input if possible.
|
||||
#
|
||||
def prompt(query)
|
||||
if (user_output and user_input)
|
||||
user_output.print("\n" + query)
|
||||
user_input.gets
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Check the return value of a yes/no prompt
|
||||
#
|
||||
def prompt_yesno(query)
|
||||
(prompt(query + " [y/N] ") =~ /^y/i) ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -96,37 +96,7 @@ class Core
|
|||
# Displays the command help banner
|
||||
#
|
||||
def cmd_help(*args)
|
||||
driver.dispatcher_stack.reverse.each { |dispatcher|
|
||||
begin
|
||||
commands = dispatcher.commands
|
||||
rescue
|
||||
commands = nil
|
||||
next
|
||||
end
|
||||
|
||||
# Display the commands
|
||||
tbl = Table.new(
|
||||
Table::Style::Default,
|
||||
'Header' => "#{dispatcher.name} Commands",
|
||||
'Columns' =>
|
||||
[
|
||||
'Command',
|
||||
'Description'
|
||||
],
|
||||
'ColProps' =>
|
||||
{
|
||||
'Command' =>
|
||||
{
|
||||
'MaxWidth' => 12
|
||||
}
|
||||
})
|
||||
|
||||
dispatcher.commands.sort.each { |c|
|
||||
tbl << c
|
||||
}
|
||||
|
||||
print(tbl.to_s)
|
||||
}
|
||||
print(driver.help_to_s)
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -11,9 +11,6 @@ module Rex
|
|||
#
|
||||
###
|
||||
module Exception
|
||||
def to_s
|
||||
"An unknown exception occurred."
|
||||
end
|
||||
end
|
||||
|
||||
class TimeoutError < Interrupt
|
||||
|
@ -35,9 +32,9 @@ end
|
|||
class RuntimeError < ::RuntimeError
|
||||
include Exception
|
||||
|
||||
def to_s
|
||||
"A runtime error occurred."
|
||||
end
|
||||
# def to_s
|
||||
# "A runtime error occurred."
|
||||
# end
|
||||
end
|
||||
|
||||
class ArgumentError < ::ArgumentError
|
||||
|
|
|
@ -33,24 +33,30 @@ module Stream
|
|||
# Writes data to the stream.
|
||||
#
|
||||
def write(buf, opts = {})
|
||||
fd.syswrite(buf)
|
||||
end
|
||||
|
||||
#
|
||||
# Reads data from the stream.
|
||||
#
|
||||
def read(length = nil, opts = {})
|
||||
length = 16384 unless length
|
||||
|
||||
fd.sysread(length)
|
||||
end
|
||||
|
||||
#
|
||||
# Shuts down the stream for reading, writing, or both.
|
||||
#
|
||||
def shutdown(how = SW_BOTH)
|
||||
fd.shutdown(how)
|
||||
end
|
||||
|
||||
#
|
||||
# Closes the stream and allows for resource cleanup
|
||||
#
|
||||
def close
|
||||
fd.close
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -58,6 +64,7 @@ module Stream
|
|||
# true if data is available for reading, otherwise false is returned.
|
||||
#
|
||||
def has_read_data?(timeout = nil)
|
||||
Rex::ThreadSafe.select([ fd ], nil, nil, timeout)
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -16,7 +16,9 @@ module IO
|
|||
###
|
||||
module StreamAbstraction
|
||||
|
||||
#
|
||||
# Creates a streaming socket pair
|
||||
#
|
||||
def initialize_abstraction
|
||||
self.lsock, self.rsock = ::Socket.pair(::Socket::AF_UNIX,
|
||||
::Socket::SOCK_STREAM, 0)
|
||||
|
|
|
@ -49,7 +49,6 @@ class Channel
|
|||
|
||||
# Valid channel context?
|
||||
if (channel == nil)
|
||||
puts "nil wtf"
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -243,6 +242,26 @@ class Channel
|
|||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Enables or disables interactive mode
|
||||
#
|
||||
def interactive(tf = true, addends = nil)
|
||||
if (self.cid == nil)
|
||||
raise IOError, "Channel has been closed.", caller
|
||||
end
|
||||
|
||||
request = Packet.create_request('core_channel_interact')
|
||||
|
||||
# Populate the request
|
||||
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
|
||||
request.add_tlv(TLV_TYPE_BOOL, tf)
|
||||
request.add_tlvs(addends)
|
||||
|
||||
self.client.send_request(request)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Direct I/O
|
||||
|
|
|
@ -24,6 +24,8 @@ module Pools
|
|||
###
|
||||
class StreamPool < Rex::Post::Meterpreter::Channels::Pool
|
||||
|
||||
include Rex::IO::StreamAbstraction
|
||||
|
||||
##
|
||||
#
|
||||
# Constructor
|
||||
|
@ -33,6 +35,8 @@ class StreamPool < Rex::Post::Meterpreter::Channels::Pool
|
|||
# Initializes the file channel instance
|
||||
def initialize(client, cid, type, flags)
|
||||
super(client, cid, type, flags)
|
||||
|
||||
initialize_abstraction
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -51,6 +55,19 @@ class StreamPool < Rex::Post::Meterpreter::Channels::Pool
|
|||
return false
|
||||
end
|
||||
|
||||
def dio_write_handler(packet, data)
|
||||
rsock.write(data)
|
||||
|
||||
return true;
|
||||
end
|
||||
|
||||
def dio_close_handler(packet)
|
||||
rsock.close
|
||||
|
||||
return super(packet)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end; end; end; end; end
|
||||
|
|
|
@ -63,6 +63,8 @@ class Config
|
|||
return ifaces
|
||||
end
|
||||
|
||||
alias interfaces get_interfaces
|
||||
|
||||
##
|
||||
#
|
||||
# Routing
|
||||
|
@ -91,6 +93,8 @@ class Config
|
|||
|
||||
return routes
|
||||
end
|
||||
|
||||
alias routes get_routes
|
||||
|
||||
# Adds a route to the target machine
|
||||
def add_route(subnet, netmask, gateway)
|
||||
|
|
|
@ -6,6 +6,24 @@ module Rex
|
|||
module Post
|
||||
module Meterpreter
|
||||
|
||||
###
|
||||
#
|
||||
# RequestError
|
||||
# ------------
|
||||
#
|
||||
# Exception thrown when a request fails.
|
||||
#
|
||||
###
|
||||
class RequestError < ArgumentError
|
||||
def initialize(method, result)
|
||||
@method = method
|
||||
@result = result
|
||||
end
|
||||
def to_s
|
||||
"#{@method}: Operation failed: #{@result}"
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# PacketDispatcher
|
||||
|
@ -44,9 +62,13 @@ module PacketDispatcher
|
|||
response = send_packet_wait_response(packet, t)
|
||||
|
||||
if (response == nil)
|
||||
raise RuntimeError, packet.method + ": No response was received.", caller
|
||||
raise TimeoutError
|
||||
elsif (response.result != 0)
|
||||
raise RuntimeError, packet.method + ": Operation failed: #{response.result}", caller
|
||||
e = RequestError.new(packet.method, response.result)
|
||||
|
||||
e.set_backtrace(caller)
|
||||
|
||||
raise e
|
||||
end
|
||||
|
||||
return response
|
||||
|
|
|
@ -19,6 +19,7 @@ class Console
|
|||
include Rex::Ui::Text::DispatcherShell
|
||||
|
||||
# Dispatchers
|
||||
require 'rex/post/meterpreter/ui/console/interactive_channel'
|
||||
require 'rex/post/meterpreter/ui/console/command_dispatcher'
|
||||
require 'rex/post/meterpreter/ui/console/command_dispatcher/core'
|
||||
|
||||
|
@ -55,6 +56,31 @@ class Console
|
|||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Interacts with the supplied channel
|
||||
#
|
||||
def interact_with_channel(channel)
|
||||
channel.extend(InteractiveChannel) unless (channel.kind_of?(InteractiveChannel) == true)
|
||||
|
||||
channel.init_ui(input, output)
|
||||
channel.interact
|
||||
channel.reset_ui
|
||||
end
|
||||
|
||||
#
|
||||
# Runs the specified command wrapper in something to catch meterpreter
|
||||
# exceptions.
|
||||
#
|
||||
def run_command(dispatcher, method, arguments)
|
||||
begin
|
||||
super
|
||||
rescue TimeoutError
|
||||
output.print_line("Operation timed out.")
|
||||
rescue RequestError => info
|
||||
output.print_line(info.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :client
|
||||
|
||||
protected
|
||||
|
|
|
@ -60,6 +60,7 @@ class Console::CommandDispatcher::Core
|
|||
# Displays the help menu
|
||||
#
|
||||
def cmd_help(*args)
|
||||
print(shell.help_to_s)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -90,11 +91,11 @@ class Console::CommandDispatcher::Core
|
|||
md = m.downcase
|
||||
|
||||
if (extensions.include?(md))
|
||||
print_error("The '#{m}' extension has already been loaded.")
|
||||
print_error("The '#{md}' extension has already been loaded.")
|
||||
next
|
||||
end
|
||||
|
||||
print("Loading extension #{m}...")
|
||||
print("Loading extension #{md}...")
|
||||
|
||||
begin
|
||||
# Use the remote side, then load the client-side
|
||||
|
|
|
@ -15,16 +15,27 @@ module Ui
|
|||
###
|
||||
class Console::CommandDispatcher::Stdapi
|
||||
|
||||
require 'rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs'
|
||||
require 'rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net'
|
||||
require 'rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys'
|
||||
|
||||
Klass = Console::CommandDispatcher::Stdapi
|
||||
|
||||
include Console::CommandDispatcher
|
||||
Dispatchers =
|
||||
[
|
||||
Klass::Fs,
|
||||
Klass::Net,
|
||||
Klass::Sys,
|
||||
]
|
||||
|
||||
require 'rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs'
|
||||
include Console::CommandDispatcher
|
||||
|
||||
def initialize(shell)
|
||||
super
|
||||
|
||||
shell.enstack_dispatcher(Klass::Fs)
|
||||
Dispatchers.each { |d|
|
||||
shell.enstack_dispatcher(d)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -7,10 +7,10 @@ module Ui
|
|||
|
||||
###
|
||||
#
|
||||
# Stdapi
|
||||
# ------
|
||||
# Fs
|
||||
# --
|
||||
#
|
||||
# Standard API extension.
|
||||
# The file system portion of the standard API extension.
|
||||
#
|
||||
###
|
||||
class Console::CommandDispatcher::Stdapi::Fs
|
||||
|
@ -207,7 +207,7 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
# Uploads a file or directory to the remote machine from the local
|
||||
# machine.
|
||||
#
|
||||
def cmd_download(*args)
|
||||
def cmd_upload(*args)
|
||||
if (args.length < 2)
|
||||
print(
|
||||
"Usage: upload [options] src1 src2 src3 ... destination\n\n" +
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
require 'rex/post/meterpreter'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Ui
|
||||
|
||||
###
|
||||
#
|
||||
# Net
|
||||
# ---
|
||||
#
|
||||
# The networking portion of the standard API extension.
|
||||
#
|
||||
###
|
||||
class Console::CommandDispatcher::Stdapi::Net
|
||||
|
||||
Klass = Console::CommandDispatcher::Stdapi::Net
|
||||
|
||||
include Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# Options for the generate command
|
||||
#
|
||||
@@route_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ])
|
||||
|
||||
#
|
||||
# List of supported commands
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"ipconfig" => "Display interfaces",
|
||||
"route" => "View and modify the routing table",
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Name for this dispatcher
|
||||
#
|
||||
def name
|
||||
"Stdapi: Networking"
|
||||
end
|
||||
|
||||
#
|
||||
# Displays interfaces on the remote machine.
|
||||
#
|
||||
def cmd_ipconfig(*args)
|
||||
ifaces = client.net.config.interfaces
|
||||
|
||||
if (ifaces.length == 0)
|
||||
print_line("No interfaces were found.")
|
||||
else
|
||||
client.net.config.each_interface { |iface|
|
||||
print("\n" + iface.pretty + "\n")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Displays or modifies the routing table on the remote machine.
|
||||
#
|
||||
def cmd_route(*args)
|
||||
# Default to list
|
||||
if (args.length == 0)
|
||||
args.unshift("list")
|
||||
end
|
||||
|
||||
# Check to see if they specified -h
|
||||
@@route_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print(
|
||||
"Usage: route [-h] command [args]\n\n" +
|
||||
"Display or modify the routing table on the remote machine.\n\n" +
|
||||
"Supported commands:\n\n" +
|
||||
" add [subnet] [netmask] [gateway]\n" +
|
||||
" delete [subnet] [netmask] [gateway]\n" +
|
||||
" list\n\n")
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
# Process the commands
|
||||
case args.shift
|
||||
when "list"
|
||||
routes = client.net.config.routes
|
||||
|
||||
if (routes.length == 0)
|
||||
print_line("No routes were found.")
|
||||
else
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "Network routes",
|
||||
'Indent' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
"Subnet",
|
||||
"Netmask",
|
||||
"Gateway"
|
||||
])
|
||||
|
||||
routes.each { |route|
|
||||
tbl << [ route.subnet, route.netmask, route.gateway ]
|
||||
}
|
||||
|
||||
print("\n" + tbl.to_s + "\n")
|
||||
end
|
||||
when "add"
|
||||
print_line("Creating route #{args[0]}/#{args[1]} -> #{args[2]}")
|
||||
|
||||
client.net.config.add_route(*args)
|
||||
when "delete"
|
||||
print_line("Deleting route #{args[0]}/#{args[1]} -> #{args[2]}")
|
||||
|
||||
client.net.config.add_route(*args)
|
||||
else
|
||||
print_error("Unsupported command: #{args[0]}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,128 @@
|
|||
require 'rex/post/meterpreter'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Ui
|
||||
|
||||
###
|
||||
#
|
||||
# Sys
|
||||
# ---
|
||||
#
|
||||
# The system level portion of the standard API extension.
|
||||
#
|
||||
###
|
||||
class Console::CommandDispatcher::Stdapi::Sys
|
||||
|
||||
Klass = Console::CommandDispatcher::Stdapi::Sys
|
||||
|
||||
include Console::CommandDispatcher
|
||||
|
||||
@@execute_opts = Rex::Parser::Arguments.new(
|
||||
"-a" => [ true, "The arguments to pass to the command." ],
|
||||
"-c" => [ false, "Channelized I/O (required for interaction)." ],
|
||||
"-f" => [ true, "The executable command to run." ],
|
||||
"-h" => [ false, "Help menu." ],
|
||||
"-H" => [ false, "Create the process hidden from view." ],
|
||||
"-i" => [ false, "Interact with the process after creating it." ])
|
||||
|
||||
#
|
||||
# List of supported commands
|
||||
#
|
||||
def commands
|
||||
{
|
||||
"ps" => "List running processes",
|
||||
"execute" => "Execute a command",
|
||||
"kill" => "Terminate a process",
|
||||
"getpid" => "Get the current process identifier",
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Name for this dispatcher
|
||||
#
|
||||
def name
|
||||
"Stdapi: System"
|
||||
end
|
||||
|
||||
#
|
||||
# Executes a command with some options.
|
||||
#
|
||||
def cmd_execute(*args)
|
||||
if (args.length == 0)
|
||||
args.unshift("-h")
|
||||
end
|
||||
|
||||
interact = false
|
||||
channelized = nil
|
||||
hidden = nil
|
||||
cmd_args = nil
|
||||
cmd_exec = nil
|
||||
|
||||
@@execute_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-a"
|
||||
cmd_args = val
|
||||
when "-c"
|
||||
channelized = true
|
||||
when "-f"
|
||||
cmd_exec = val
|
||||
when "-H"
|
||||
hidden = true
|
||||
when "-h"
|
||||
print(
|
||||
"Usage: execute -f file [options]\n\n" +
|
||||
"Executes a command on the remote machine.\n" +
|
||||
@@execute_opts.usage)
|
||||
return true
|
||||
when "-i"
|
||||
channelized = true
|
||||
interact = true
|
||||
end
|
||||
}
|
||||
|
||||
# Did we at least get an executable?
|
||||
if (cmd_exec == nil)
|
||||
print_error("You must specify an executable file with -f")
|
||||
return true
|
||||
end
|
||||
|
||||
# Execute it
|
||||
p = client.sys.process.execute(cmd_exec, cmd_args,
|
||||
'Channelized' => channelized,
|
||||
'Hidden' => hidden)
|
||||
|
||||
print_line("Process #{p.pid} created.")
|
||||
print_line("Channel #{p.channel.cid} created.") if (p.channel)
|
||||
|
||||
if (interact and p.channel)
|
||||
shell.interact_with_channel(p.channel)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the process identifier that meterpreter is running in on the remote
|
||||
# machine.
|
||||
#
|
||||
def cmd_getpid(*args)
|
||||
end
|
||||
|
||||
#
|
||||
# Kills one or more processes.
|
||||
#
|
||||
def cmd_kill(*args)
|
||||
end
|
||||
|
||||
#
|
||||
# Lists running processes
|
||||
#
|
||||
def cmd_ps(*args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,94 @@
|
|||
module Rex
|
||||
module Post
|
||||
module Meterpreter
|
||||
module Ui
|
||||
|
||||
###
|
||||
#
|
||||
# InteractiveChannel
|
||||
# ------------------
|
||||
#
|
||||
# Mixin that is meant to extend the base channel class from meterpreter in a
|
||||
# manner that adds interactive capabilities.
|
||||
#
|
||||
###
|
||||
module Console::InteractiveChannel
|
||||
|
||||
include Rex::Ui::Interactive
|
||||
|
||||
#
|
||||
# Interacts with self.
|
||||
#
|
||||
def _interact
|
||||
# If the channel has a left-side socket, then we can interact with it.
|
||||
if (self.lsock)
|
||||
self.interactive(true)
|
||||
|
||||
begin
|
||||
interact_stream(self)
|
||||
rescue
|
||||
end
|
||||
|
||||
self.interactive(false)
|
||||
else
|
||||
print_error("Channel #{self.cid} does not support interaction.")
|
||||
|
||||
self.interacting = false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Called when an interrupt is sent
|
||||
#
|
||||
def _interrupt
|
||||
prompt_yesno("Terminate channel #{self.cid}?")
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
def _suspend
|
||||
# Ask the user if they would like to background the session
|
||||
if (prompt_yesno("Background channel #{self.cid}?") == true)
|
||||
self.interacting = false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Closes the channel like it aint no thang.
|
||||
#
|
||||
def _interact_complete
|
||||
self.close
|
||||
end
|
||||
|
||||
#
|
||||
# Reads data from local input and writes it remotely.
|
||||
#
|
||||
def _stream_read_local_write_remote(channel)
|
||||
data = user_input.gets
|
||||
|
||||
self.write(data)
|
||||
end
|
||||
|
||||
#
|
||||
# Reads from the channel and writes locally.
|
||||
#
|
||||
def _stream_read_remote_write_local(channel)
|
||||
data = channel.read
|
||||
|
||||
user_output.print(data)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the remote file descriptor to select on
|
||||
#
|
||||
def _remote_fd
|
||||
self.lsock
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,3 +17,4 @@ require 'rex/ui/text/table'
|
|||
|
||||
# Ui subscriber
|
||||
require 'rex/ui/subscriber'
|
||||
require 'rex/ui/interactive'
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
module Rex
|
||||
module Ui
|
||||
|
||||
###
|
||||
#
|
||||
# Interactive
|
||||
# -----------
|
||||
#
|
||||
# This class implements the stubs that are needed to provide an interactive
|
||||
# user interface that is backed against something arbitrary.
|
||||
#
|
||||
###
|
||||
module Interactive
|
||||
|
||||
#
|
||||
# Interactive sessions by default may interact with the local user input
|
||||
# and output.
|
||||
#
|
||||
include Rex::Ui::Subscriber
|
||||
|
||||
#
|
||||
# Starts interacting with the session at the most raw level, simply
|
||||
# forwarding input from user_input to rstream and forwarding input from
|
||||
# rstream to user_output.
|
||||
#
|
||||
def interact
|
||||
self.interacting = true
|
||||
|
||||
eof = false
|
||||
|
||||
# Handle suspend notifications
|
||||
handle_suspend
|
||||
|
||||
callcc { |ctx|
|
||||
# As long as we're interacting...
|
||||
while (self.interacting == true)
|
||||
begin
|
||||
_interact
|
||||
# If we get an interrupt exception, ask the user if they want to
|
||||
# abort the interaction. If they do, then we return out of
|
||||
# the interact function and call it a day.
|
||||
rescue Interrupt
|
||||
if (_interrupt)
|
||||
eof = true
|
||||
ctx.call
|
||||
end
|
||||
# If we reach EOF or the connection is reset...
|
||||
rescue EOFError, Errno::ECONNRESET
|
||||
eof = true
|
||||
ctx.call
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
# Restore the suspend handler
|
||||
restore_suspend
|
||||
|
||||
# If we've hit eof, call the interact complete handler
|
||||
_interact_complete if (eof == true)
|
||||
|
||||
# Return whether or not EOF was reached
|
||||
return eof
|
||||
end
|
||||
|
||||
#
|
||||
# Whether or not the session is currently being interacted with
|
||||
#
|
||||
attr_reader :interacting
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :interacting
|
||||
#
|
||||
# The original suspend proc.
|
||||
#
|
||||
attr_accessor :orig_suspend
|
||||
|
||||
#
|
||||
# Stub method that is meant to handler interaction
|
||||
#
|
||||
def _interact
|
||||
end
|
||||
|
||||
#
|
||||
# Called when an interrupt is sent.
|
||||
#
|
||||
def _interrupt
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a suspend is sent.
|
||||
#
|
||||
def _suspend
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Called when interaction has completed and one of the sides has closed.
|
||||
#
|
||||
def _interact_complete
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Read from remote and write to local.
|
||||
#
|
||||
def _stream_read_remote_write_local(stream)
|
||||
data = stream.get
|
||||
|
||||
user_output.print(data)
|
||||
end
|
||||
|
||||
#
|
||||
# Read from local and write to remote.
|
||||
#
|
||||
def _stream_read_local_write_remote(stream)
|
||||
data = user_input.gets
|
||||
|
||||
stream.put(data)
|
||||
end
|
||||
|
||||
def _local_fd
|
||||
user_input.fd
|
||||
end
|
||||
|
||||
def _remote_fd(stream)
|
||||
stream.fd
|
||||
end
|
||||
|
||||
#
|
||||
# Interacts with two streaming connections, reading data from one and
|
||||
# writing it to the other. Both are expected to implement Rex::IO::Stream.
|
||||
#
|
||||
def interact_stream(stream)
|
||||
while self.interacting
|
||||
# Select input and rstream
|
||||
sd = Rex::ThreadSafe.select([ _local_fd, _remote_fd(stream) ])
|
||||
|
||||
# Cycle through the items that have data
|
||||
# From the stream? Write to user_output.
|
||||
sd[0].each { |s|
|
||||
if (s == _remote_fd(stream))
|
||||
_stream_read_remote_write_local(stream)
|
||||
# From user_input? Write to stream.
|
||||
elsif (s == _local_fd)
|
||||
_stream_read_local_write_remote(stream)
|
||||
end
|
||||
} if (sd)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Installs a signal handler to monitor suspend signal notifications.
|
||||
#
|
||||
def handle_suspend
|
||||
if (orig_suspend == nil)
|
||||
self.orig_suspend = Signal.trap("TSTP") {
|
||||
_suspend
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Restores the previously installed signal handler for suspend
|
||||
# notifications.
|
||||
#
|
||||
def restore_suspend
|
||||
if (orig_suspend)
|
||||
Signal.trap("TSTP", orig_suspend)
|
||||
|
||||
self.orig_suspend = nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Prompt the user for input if possible.
|
||||
#
|
||||
def prompt(query)
|
||||
if (user_output and user_input)
|
||||
user_output.print("\n" + query)
|
||||
user_input.gets
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Check the return value of a yes/no prompt
|
||||
#
|
||||
def prompt_yesno(query)
|
||||
(prompt(query + " [y/N] ") =~ /^y/i) ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -101,7 +101,9 @@ module DispatcherShell
|
|||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Run a single command line
|
||||
#
|
||||
def run_single(line)
|
||||
arguments = parse_line(line)
|
||||
method = arguments.shift
|
||||
|
@ -115,10 +117,9 @@ module DispatcherShell
|
|||
dispatcher_stack.each { |dispatcher|
|
||||
begin
|
||||
if (dispatcher.respond_to?('cmd_' + method))
|
||||
run_command(dispatcher, method, arguments)
|
||||
|
||||
found = true
|
||||
eval("
|
||||
dispatcher.#{'cmd_' + method}(*arguments)
|
||||
")
|
||||
end
|
||||
rescue
|
||||
output.print_error(
|
||||
|
@ -139,6 +140,13 @@ module DispatcherShell
|
|||
return found
|
||||
end
|
||||
|
||||
#
|
||||
# Runs the supplied command on the given dispatcher.
|
||||
#
|
||||
def run_command(dispatcher, method, arguments)
|
||||
eval("dispatcher.#{'cmd_' + method}(*arguments)")
|
||||
end
|
||||
|
||||
#
|
||||
# If the command is unknown...
|
||||
#
|
||||
|
@ -146,16 +154,58 @@ module DispatcherShell
|
|||
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
|
||||
|
||||
#
|
||||
# Return a readable version of a help banner for all of the enstacked
|
||||
# dispatchers.
|
||||
#
|
||||
def help_to_s(opts = {})
|
||||
str = ''
|
||||
|
||||
dispatcher_stack.reverse.each { |dispatcher|
|
||||
# No commands? Suckage.
|
||||
next if ((dispatcher.respond_to?('commands') == false) or
|
||||
(dispatcher.commands.length == 0))
|
||||
|
||||
# Display the commands
|
||||
tbl = Table.new(
|
||||
'Header' => "#{dispatcher.name} Commands",
|
||||
'Indent' => opts['Indent'] || 4,
|
||||
'Columns' =>
|
||||
[
|
||||
'Command',
|
||||
'Description'
|
||||
],
|
||||
'ColProps' =>
|
||||
{
|
||||
'Command' =>
|
||||
{
|
||||
'MaxWidth' => 12
|
||||
}
|
||||
})
|
||||
|
||||
dispatcher.commands.sort.each { |c|
|
||||
tbl << c
|
||||
}
|
||||
|
||||
str += "\n" + tbl.to_s + "\n"
|
||||
}
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
attr_accessor :dispatcher_stack
|
||||
|
|
Loading…
Reference in New Issue