mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-05 14:57:30 +01:00
pivoting with portfwd command
git-svn-id: file:///home/svn/incoming/trunk@2916 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
550080bcfb
commit
6ec3700b16
@ -1,86 +1,5 @@
|
||||
X - evasion class
|
||||
X - set_level(evlvl)
|
||||
X - high?
|
||||
X - medium?
|
||||
- testing framework
|
||||
- run all the exploits through all the diff payload handler permutations
|
||||
- simulate clients for each different permutation
|
||||
X - seh exploit mixin
|
||||
X - generate padded registration records
|
||||
X - move jump around
|
||||
X - use multi-size jump
|
||||
- return address pool
|
||||
- exploits say what modules they have present
|
||||
- target says what platform is being exploited
|
||||
- target says what type of instruction is viable
|
||||
- pool returns a random return address for that target
|
||||
- automatic opcode db synchronization
|
||||
- add module meta-info
|
||||
- payloads
|
||||
- calling convention (staged shell is incompat with ord stagers)
|
||||
- stack requirements
|
||||
- etc
|
||||
- exploit reloading
|
||||
- payload convention
|
||||
- make it so stages/stagers are queried for compatibility
|
||||
- make it so exploits query convention compat
|
||||
- ws2ord stuff
|
||||
- make it possible to share handlers?
|
||||
- payloads that use the same handlers could be shared
|
||||
This file contains things that need to be done that aren't in the plan:
|
||||
|
||||
X - switch to x86 from ia32
|
||||
X - exploit kick-off
|
||||
X - payload generation
|
||||
X - generate payload for target
|
||||
X - encoder payload for target
|
||||
X - loop encoders on failure
|
||||
X - pad nops
|
||||
X - handler init
|
||||
X - setup handler
|
||||
X - start handler
|
||||
X - exploit
|
||||
X - call exploit
|
||||
X ... wait for session ...
|
||||
X - handler cleanup
|
||||
X - stop handler
|
||||
X - cleanup handler
|
||||
X -
|
||||
X
|
||||
X - add the concept of services to framework:
|
||||
X - instead, just make it a singleton, doesn't belong on framework
|
||||
X - add port forward service
|
||||
X
|
||||
X# first parameter is class that must inherit from Rex::Proto so that it has .alias
|
||||
Xservice = framework.services.start(Rex::Proto::HTTP::Server, 'Port' => 80, 'Host' => '127.0.0.1')
|
||||
Xservice = framework.services['HTTP Server']
|
||||
X
|
||||
Xoverrides any existing resource handler with this name:
|
||||
Xservice.create_resource("/uri", Proc.new { |conn, request|
|
||||
X})
|
||||
X
|
||||
Xservice.remove_resource("/uri")
|
||||
Xservice.shutdown
|
||||
X ^- reference counted, only terminates when reference count drops to zero
|
||||
X
|
||||
X- exploit mixins
|
||||
X - Http
|
||||
X - Http::Client
|
||||
X connect
|
||||
X create_request
|
||||
X send_request
|
||||
X handler
|
||||
X - Http::Server
|
||||
X handle_request(req)
|
||||
X create_response
|
||||
X send_response
|
||||
X- findsock payloads
|
||||
X - findsock handler
|
||||
- meterpreter
|
||||
X - more ui wrapping
|
||||
X - fix route addition/removal in stdapi server dll (mib structure issue)
|
||||
X - fix interactive stream pool channels
|
||||
X - make migrate on server not open with PROCESS_ALL_ACCESS
|
||||
N - dupe input instance when passing to sessions
|
||||
X - fix module loading order
|
||||
X - problems with dllinject getting loaded after meterpreter due to dependencies
|
||||
X - fix default handle inheritance in meterp process execution
|
||||
- revisit pivoting
|
||||
- connections seemed slow
|
||||
- data transfers seemed slow
|
||||
|
@ -1,9 +1,10 @@
|
||||
The following things are required for the December alpha release:
|
||||
|
||||
- rex
|
||||
- post-exploitation
|
||||
- meterpreter
|
||||
- pivoting
|
||||
X - post-exploitation
|
||||
X - meterpreter
|
||||
X - pivoting
|
||||
X - portfwd command
|
||||
- networking
|
||||
- switch board routing table for pivoting
|
||||
- meterpreter 'comm' support
|
||||
|
@ -165,7 +165,9 @@ module Exploit::Remote::TcpServer
|
||||
#
|
||||
def stop_service
|
||||
if (service)
|
||||
self.service.stop
|
||||
Rex::ServiceManager.stop_service(self.service)
|
||||
|
||||
self.service.deref
|
||||
self.service = nil
|
||||
end
|
||||
end
|
||||
|
@ -16,3 +16,5 @@ LEV_0 = 0
|
||||
LEV_1 = 1
|
||||
LEV_2 = 2
|
||||
LEV_3 = 3
|
||||
|
||||
|
||||
|
@ -65,6 +65,25 @@ class AmbiguousArgumentError < ::RuntimeError
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This error is thrown when a stream is detected as being closed.
|
||||
#
|
||||
class StreamClosedError < ::IOError
|
||||
include Exception
|
||||
|
||||
def initialize(stream)
|
||||
@stream = stream
|
||||
end
|
||||
|
||||
def stream
|
||||
@stream
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Stream #{@stream} is closed."
|
||||
end
|
||||
end
|
||||
|
||||
#####
|
||||
#####
|
||||
##
|
||||
|
@ -21,6 +21,7 @@ class UnitTest < Test::Unit::TestCase
|
||||
|
||||
begin
|
||||
raise mod.new
|
||||
rescue ::ArgumentError
|
||||
rescue mod => detail
|
||||
assert_respond_to(detail, 'to_s', "#{mod} does not implement to_s")
|
||||
assert_not_nil(detail.to_s, "invalid to_s")
|
||||
|
@ -76,7 +76,7 @@ class Socket
|
||||
# representation of the left side of the socket for
|
||||
# the caller to use
|
||||
if (channel != nil)
|
||||
res = Rex::Socket::Stream.new(channel.lsock)
|
||||
res = channel.lsock
|
||||
end
|
||||
elsif (params.udp?)
|
||||
if (params.server?)
|
||||
@ -126,7 +126,25 @@ protected
|
||||
while (1)
|
||||
|
||||
# Watch for data
|
||||
socks = select(monitored_sockets, nil, nil, 1)
|
||||
begin
|
||||
socks = select(monitored_sockets, nil, nil, 1)
|
||||
rescue StreamClosedError => e
|
||||
channel = monitored_socket_channels[e.stream.object_id]
|
||||
|
||||
dlog("monitor_channels: channel #{channel} closed (#{e.stream})",
|
||||
'rex', LEV_3)
|
||||
|
||||
if (channel)
|
||||
begin
|
||||
channel.close
|
||||
rescue IOError
|
||||
end
|
||||
|
||||
remove_monitored_socket(e.stream)
|
||||
end
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
# No data?
|
||||
if (socks == nil || socks[0] == nil)
|
||||
|
@ -1,4 +1,5 @@
|
||||
require 'rex/post/meterpreter'
|
||||
require 'rex/service_manager'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
@ -20,11 +21,21 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||
include Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# Options for the generate command
|
||||
# Options for the route command
|
||||
#
|
||||
@@route_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ])
|
||||
|
||||
#
|
||||
# Options for the portfwd command
|
||||
#
|
||||
@@portfwd_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-l" => [ true, "The local port to listen on." ],
|
||||
"-r" => [ true, "The remote host to connect to." ],
|
||||
"-p" => [ true, "The remote port to connect to." ],
|
||||
"-L" => [ true, "The local host to listen on (optional)." ])
|
||||
|
||||
#
|
||||
# List of supported commands
|
||||
#
|
||||
@ -32,6 +43,7 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||
{
|
||||
"ipconfig" => "Display interfaces",
|
||||
"route" => "View and modify the routing table",
|
||||
"portfwd" => "Forward a local port to a remote service",
|
||||
}
|
||||
end
|
||||
|
||||
@ -118,6 +130,132 @@ class Console::CommandDispatcher::Stdapi::Net
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Starts and stops local port forwards to remote hosts on the target
|
||||
# network. This provides an elementary pivoting interface.
|
||||
#
|
||||
def cmd_portfwd(*args)
|
||||
args.unshift("list") unless args.length
|
||||
|
||||
# For clarity's sake.
|
||||
lport = nil
|
||||
lhost = nil
|
||||
rport = nil
|
||||
rhost = nil
|
||||
|
||||
# Parse the options
|
||||
@@portfwd_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
print(
|
||||
"Usage: route [-h] [add / delete / list] [args]\n\n" +
|
||||
@@portfwd_opts.usage)
|
||||
return true
|
||||
when "-l"
|
||||
lport = val.to_i
|
||||
when "-L"
|
||||
lhost = val
|
||||
when "-p"
|
||||
rport = val.to_i
|
||||
when "-r"
|
||||
rhost = val
|
||||
end
|
||||
}
|
||||
|
||||
# Process the command
|
||||
case args.shift
|
||||
when "list"
|
||||
|
||||
# Get the service context
|
||||
service = Rex::ServiceManager.start(Rex::Services::LocalRelay)
|
||||
|
||||
begin
|
||||
|
||||
cnt = 0
|
||||
|
||||
# Enumerate each TCP relay
|
||||
service.each_tcp_relay { |lhost, lport, rhost, rport, opts|
|
||||
next if (opts['MeterpreterRelay'] == nil)
|
||||
|
||||
print_line("#{cnt}: #{lhost}:#{lport} -> #{rhost}:#{rport}")
|
||||
|
||||
cnt += 1
|
||||
}
|
||||
|
||||
print_line
|
||||
print_line("#{cnt} total local port forwards.")
|
||||
|
||||
ensure
|
||||
service.deref
|
||||
end
|
||||
|
||||
when "add"
|
||||
|
||||
# Validate parameters
|
||||
if (!lport or !rhost or !rport)
|
||||
print_error("You must supply a local port, remote host, and remote port.")
|
||||
return
|
||||
end
|
||||
|
||||
# Build a local port forward in association with the channel
|
||||
service = Rex::ServiceManager.start(Rex::Services::LocalRelay)
|
||||
|
||||
begin
|
||||
# Start the local TCP relay in association with this stream
|
||||
service.start_tcp_relay(lport,
|
||||
'LocalHost' => lhost,
|
||||
'PeerHost' => rhost,
|
||||
'PeerPort' => rport,
|
||||
'MeterpreterRelay' => true,
|
||||
'OnLocalConnection' => Proc.new { |relay, lfd|
|
||||
create_tcp_channel(relay)
|
||||
})
|
||||
|
||||
print_status("Local TCP relay created: #{lhost || '0.0.0.0'}:#{lport} <-> #{rhost}:#{rport}")
|
||||
|
||||
ensure
|
||||
# Lost our reference to the service now that we're done with it
|
||||
service.deref
|
||||
end
|
||||
|
||||
# Delete local port forwards
|
||||
when "delete"
|
||||
|
||||
# No local port, no love.
|
||||
if (!lport)
|
||||
print_error("You must supply a local port.")
|
||||
return
|
||||
end
|
||||
|
||||
service = Rex::ServiceManager.start(Rex::Services::LocalRelay)
|
||||
|
||||
# Stop the service
|
||||
begin
|
||||
if (service.stop_tcp_relay(lport, lhost))
|
||||
print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
else
|
||||
print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}")
|
||||
end
|
||||
ensure
|
||||
service.deref
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Creates a TCP channel using the supplied relay context.
|
||||
#
|
||||
def create_tcp_channel(relay)
|
||||
client.net.socket.create(
|
||||
Rex::Socket::Parameters.new(
|
||||
'PeerHost' => relay.opts['PeerHost'],
|
||||
'PeerPort' => relay.opts['PeerPort'],
|
||||
'Proto' => 'tcp'))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -16,12 +16,17 @@ module Rex
|
||||
module Service
|
||||
include Ref
|
||||
|
||||
require 'rex/services/local_relay'
|
||||
|
||||
#
|
||||
# Calls stop on the service once the ref count drops.
|
||||
#
|
||||
def cleanup
|
||||
stop
|
||||
end
|
||||
|
||||
attr_accessor :alias
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -30,8 +30,16 @@ class ServiceManager < Hash
|
||||
#
|
||||
# Calls the instance method to stop a service.
|
||||
#
|
||||
def self.stop(als)
|
||||
self.instance.stop(als)
|
||||
def self.stop(klass, *args)
|
||||
self.instance.stop(klass, *args)
|
||||
end
|
||||
|
||||
def self.stop_by_alias(als)
|
||||
self.instance.stop_by_alias(als)
|
||||
end
|
||||
|
||||
def self.stop_service(service)
|
||||
self.instance.stop_service(service)
|
||||
end
|
||||
|
||||
#
|
||||
@ -39,7 +47,7 @@ class ServiceManager < Hash
|
||||
#
|
||||
def start(klass, *args)
|
||||
# Get the hardcore alias.
|
||||
hals = "__#{klass.name}#{args.to_s}"
|
||||
hals = hardcore_alias(klass, *args)
|
||||
|
||||
# Has a service already been constructed for this guy? If so, increment
|
||||
# its reference count like it aint no thang.
|
||||
@ -70,22 +78,59 @@ class ServiceManager < Hash
|
||||
|
||||
# Alias associate and initialize reference counting
|
||||
self[als] = self[hals] = inst.refinit
|
||||
|
||||
# Pass the caller a reference
|
||||
inst.ref
|
||||
|
||||
inst
|
||||
end
|
||||
|
||||
#
|
||||
# Stop a service using a given klass and arguments. These should mirror
|
||||
# what was originally passed to start exactly. If the reference count of
|
||||
# the service drops to zero the service will be destroyed.
|
||||
#
|
||||
def stop(klass, *args)
|
||||
stop_service(hals[hardcore_alias(klass, *args)])
|
||||
end
|
||||
|
||||
#
|
||||
# Stops a service using the provided alias
|
||||
#
|
||||
def stop(als)
|
||||
def stop_by_alias(als)
|
||||
stop_service(self[als])
|
||||
end
|
||||
|
||||
#
|
||||
# Stops a service instance.
|
||||
#
|
||||
def stop_service(inst)
|
||||
# Stop the service and be done wif it, but only if the number of
|
||||
# references has dropped to zero
|
||||
if ((inst = self[als]) and
|
||||
(inst.deref))
|
||||
if (inst)
|
||||
# Since the instance may have multiple aliases, scan through
|
||||
# all the pairs for matching stuff.
|
||||
self.each_pair { |cals, cinst|
|
||||
self.delete(cals) if (inst == cinst)
|
||||
}
|
||||
|
||||
# Lose the list-held reference to the instance
|
||||
inst.deref
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Return false if the service isn't there
|
||||
return false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Returns the alias for a given service instance.
|
||||
#
|
||||
def hardcore_alias(klass, *args)
|
||||
"__#{klass.name}#{args.to_s}"
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -22,9 +22,9 @@ class Rex::ServiceManager::UnitTest < Test::Unit::TestCase
|
||||
assert_equal("HTTP Server", s.alias)
|
||||
assert_equal("HTTP Server 1", z.alias)
|
||||
ensure
|
||||
c.stop(s.alias) if (s)
|
||||
c.stop(z.alias) if (z)
|
||||
c.stop(t.alias) if (t)
|
||||
c.stop_by_alias(s.alias) if (s)
|
||||
c.stop_by_alias(z.alias) if (z)
|
||||
c.stop_by_alias(t.alias) if (t)
|
||||
end
|
||||
|
||||
end
|
||||
|
404
lib/rex/services/local_relay.rb
Normal file
404
lib/rex/services/local_relay.rb
Normal file
@ -0,0 +1,404 @@
|
||||
require 'thread'
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Services
|
||||
|
||||
###
|
||||
#
|
||||
# LocalRelay
|
||||
# ----------
|
||||
#
|
||||
# This service acts as a local TCP relay whereby clients can connect to a
|
||||
# local listener that forwards to an arbitrary remote endpoint. Interaction
|
||||
# with the remote endpoint socket requires that it implement the
|
||||
# Rex::IO::Stream interface.
|
||||
#
|
||||
###
|
||||
class LocalRelay
|
||||
|
||||
include Rex::Service
|
||||
|
||||
###
|
||||
#
|
||||
# Stream
|
||||
# ------
|
||||
#
|
||||
# This module is used to extend streams such that they can be associated
|
||||
# with a relay context and the other side of the stream.
|
||||
#
|
||||
###
|
||||
module Stream
|
||||
|
||||
#
|
||||
# This method is called when the other side has data that has been read
|
||||
# in.
|
||||
#
|
||||
def on_other_data(data)
|
||||
if (relay.on_other_data_proc)
|
||||
relay.on_other_data_proc.call(relay, self, data)
|
||||
# By default, simply push all the data to our side.
|
||||
else
|
||||
put(data)
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :relay
|
||||
attr_accessor :other_stream
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# StreamServer
|
||||
# ------------
|
||||
#
|
||||
# This module is used to extend stream servers such that they can be
|
||||
# associated with a relay context.
|
||||
#
|
||||
###
|
||||
module StreamServer
|
||||
|
||||
#
|
||||
# This method is called when the stream server receives a local
|
||||
# connection such that the remote half can be allocated. The return
|
||||
# value of the callback should be a Stream instance.
|
||||
#
|
||||
def on_local_connection(relay, lfd)
|
||||
if (relay.on_local_connection_proc)
|
||||
relay.on_local_connection_proc.call(relay, lfd)
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :relay
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
#
|
||||
# Relay
|
||||
# -----
|
||||
#
|
||||
# This class acts as an instance of a given local relay.
|
||||
#
|
||||
###
|
||||
class Relay
|
||||
|
||||
def initialize(name, listener, opts = {})
|
||||
self.name = name
|
||||
self.listener = listener
|
||||
self.opts = opts
|
||||
self.on_local_connection_proc = opts['OnLocalConnection']
|
||||
self.on_other_data_proc = opts['OnOtherData']
|
||||
end
|
||||
|
||||
def shutdown
|
||||
listener.shutdown if (listener)
|
||||
end
|
||||
|
||||
def close
|
||||
listener.close if (listener)
|
||||
listener = nil
|
||||
end
|
||||
|
||||
attr_reader :name, :listener, :opts
|
||||
attr_accessor :on_local_connection_proc
|
||||
attr_accessor :on_other_data_proc
|
||||
protected
|
||||
attr_writer :name, :listener, :opts
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the local tcp relay monitor.
|
||||
#
|
||||
def initialize
|
||||
self.relays = Hash.new
|
||||
self.rfds = Array.new
|
||||
self.relay_thread = nil
|
||||
self.relay_mutex = Mutex.new
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Service interface implementors
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Returns the alias for this service.
|
||||
#
|
||||
def alias
|
||||
super || "Local Relay"
|
||||
end
|
||||
|
||||
#
|
||||
# Starts the thread that monitors the local relays.
|
||||
#
|
||||
def start
|
||||
self.relay_thread = Thread.new {
|
||||
begin
|
||||
monitor_relays
|
||||
rescue
|
||||
elog("Error in #{self} monitor_relays: #{$!}", 'rex')
|
||||
end
|
||||
} if (!self.relay_thread)
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the thread that monitors the local relays and destroys all local
|
||||
# listeners.
|
||||
#
|
||||
def stop
|
||||
if (self.relay_thread)
|
||||
self.relay_thread.kill
|
||||
self.relay_thread = nil
|
||||
end
|
||||
|
||||
self.relay_mutex.synchronize {
|
||||
self.relays.delete_if { |k, v|
|
||||
v.shutdown
|
||||
v.close
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
# Flush the relay list and read fd list
|
||||
self.relays.clear
|
||||
self.rfds.clear
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Adding/removing local tcp relays
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Starts a local TCP relay.
|
||||
#
|
||||
def start_tcp_relay(lport, opts = {})
|
||||
# Make sure our options are valid
|
||||
if (opts['PeerHost'] == nil or opts['PeerPort'] == nil)
|
||||
raise ArgumentError, "Missing peer host or peer port.", caller
|
||||
end
|
||||
|
||||
listener = Rex::Socket.create_tcp_server(
|
||||
'LocalHost' => opts['LocalHost'],
|
||||
'LocalPort' => lport)
|
||||
|
||||
opts['LocalPort'] = lport
|
||||
opts['__RelayType'] = 'tcp'
|
||||
|
||||
start_relay(listener, lport.to_s + (opts['LocalHost'] || '0.0.0.0'), opts)
|
||||
end
|
||||
|
||||
#
|
||||
# Starts a local relay on the supplied local port. This listener will call
|
||||
# the supplied callback procedures when various events occur.
|
||||
#
|
||||
def start_relay(stream_server, name, opts = {})
|
||||
# Create a Relay instance with the local stream and remote stream
|
||||
relay = Relay.new(name, stream_server, opts)
|
||||
|
||||
# Extend the stream_server so that we can associate it with this relay
|
||||
stream_server.extend(StreamServer)
|
||||
stream_server.relay = relay
|
||||
|
||||
# Add the stream associations the appropriate lists and hashes
|
||||
self.relay_mutex.synchronize {
|
||||
self.relays[name] = relay
|
||||
|
||||
self.rfds << stream_server
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Stops relaying on a given local port.
|
||||
#
|
||||
def stop_tcp_relay(lport, lhost = nil)
|
||||
stop_relay(lport.to_s + (lhost || '0.0.0.0'))
|
||||
end
|
||||
|
||||
#
|
||||
# Stops a relay with a given name.
|
||||
#
|
||||
def stop_relay(name)
|
||||
rv = false
|
||||
|
||||
self.relay_mutex.synchronize {
|
||||
relay = self.relays[name]
|
||||
|
||||
if (relay)
|
||||
close_relay(relay)
|
||||
rv = true
|
||||
end
|
||||
}
|
||||
|
||||
rv
|
||||
end
|
||||
|
||||
#
|
||||
# Enumerate each TCP relay
|
||||
#
|
||||
def each_tcp_relay(&block)
|
||||
self.relays.each_pair { |name, relay|
|
||||
next if (relay.opts['__RelayType'] != 'tcp')
|
||||
|
||||
yield(
|
||||
relay.opts['LocalHost'] || '0.0.0.0',
|
||||
relay.opts['LocalPort'],
|
||||
relay.opts['PeerHost'],
|
||||
relay.opts['PeerPort'],
|
||||
relay.opts)
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :relays, :relay_thread, :relay_mutex
|
||||
attr_accessor :rfds
|
||||
|
||||
#
|
||||
# Closes an cleans up a specific relay
|
||||
#
|
||||
def close_relay(relay)
|
||||
self.rfds.delete(relay.listener)
|
||||
self.relays.delete(relay.name)
|
||||
|
||||
relay.shutdown
|
||||
relay.close
|
||||
end
|
||||
|
||||
#
|
||||
# Closes a specific relay connection without tearing down the actual relay
|
||||
# itself.
|
||||
#
|
||||
def close_relay_conn(fd)
|
||||
relay = fd.relay
|
||||
ofd = fd.other_stream
|
||||
|
||||
self.rfds.delete(fd)
|
||||
|
||||
begin
|
||||
fd.shutdown
|
||||
fd.close
|
||||
rescue IOError
|
||||
end
|
||||
|
||||
if (ofd)
|
||||
self.rfds.delete(ofd)
|
||||
|
||||
begin
|
||||
ofd.shutdown
|
||||
ofd.close
|
||||
rescue IOError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Accepts a client connection on a local relay.
|
||||
#
|
||||
def accept_relay_conn(srvfd)
|
||||
relay = srvfd.relay
|
||||
|
||||
begin
|
||||
dlog("Accepting relay client connection...", 'rex', LEV_3)
|
||||
|
||||
# Accept the child connection
|
||||
lfd = srvfd.accept
|
||||
dlog("Got left side of relay: #{lfd}", 'rex', LEV_3)
|
||||
|
||||
# Call the relay's on_local_connection method which should return a
|
||||
# remote connection on success
|
||||
rfd = srvfd.on_local_connection(relay, lfd)
|
||||
dlog("Got right side of relay: #{lfd}", 'rex', LEV_3)
|
||||
rescue
|
||||
wlog("Failed to get remote half of local connection on relay #{relay.name}: #{$!}", 'rex')
|
||||
end
|
||||
|
||||
# If we have both sides, then we rock. Extend the instances, associate
|
||||
# them with the relay, associate them with each other, and add them to
|
||||
# the list of polling file descriptors
|
||||
if (lfd and rfd)
|
||||
lfd.extend(Stream)
|
||||
rfd.extend(Stream)
|
||||
|
||||
lfd.relay = relay
|
||||
rfd.relay = relay
|
||||
|
||||
lfd.other_stream = rfd
|
||||
rfd.other_stream = lfd
|
||||
|
||||
self.rfds << lfd
|
||||
self.rfds << rfd
|
||||
# Otherwise, we don't have both sides, we'll close them.
|
||||
else
|
||||
close_relay_conn(lfd)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Monitors the relays for data and passes it in both directions.
|
||||
#
|
||||
def monitor_relays
|
||||
begin
|
||||
|
||||
# Poll all the streams...
|
||||
begin
|
||||
socks = select(rfds, nil, nil, 0.2)
|
||||
rescue StreamClosedError => e
|
||||
dlog("monitor_relays: closing stream #{e.stream}", 'rex', LEV_3)
|
||||
|
||||
# Close the relay connection that is associated with the stream
|
||||
# closed error
|
||||
if (e.stream.kind_of?(Stream))
|
||||
close_relay_conn(e.stream)
|
||||
end
|
||||
|
||||
dlog("monitor_relays: closed stream #{e.stream}", 'rex', LEV_3)
|
||||
|
||||
next
|
||||
rescue
|
||||
elog("Error in #{self} monitor_relays select: #{$!}", 'rex')
|
||||
return
|
||||
end
|
||||
|
||||
# If socks is nil, go again.
|
||||
next unless socks
|
||||
|
||||
# Process read-ready file descriptors, if any.
|
||||
socks[0].each { |rfd|
|
||||
|
||||
# If this file descriptor is a server, accept the connection
|
||||
if (rfd.kind_of?(StreamServer))
|
||||
accept_relay_conn(rfd)
|
||||
# Otherwise, it's a relay connection, read data from one side
|
||||
# and write it to the other
|
||||
else
|
||||
begin
|
||||
# Read from the read fd
|
||||
data = rfd.sysread(16384)
|
||||
|
||||
dlog("monitor_relays: sending #{data.length} bytes from #{rfd} to #{rfd.other_stream}",
|
||||
'rex', LEV_3)
|
||||
|
||||
# Pass the data onto the other fd, most likely writing it.
|
||||
rfd.other_stream.on_other_data(data)
|
||||
# If we catch an EOFError, close the relay connection.
|
||||
rescue EOFError
|
||||
close_relay_conn(rfd)
|
||||
rescue
|
||||
elog("Error in #{self} monitor_relays read: #{$!}", 'rex')
|
||||
end
|
||||
end
|
||||
|
||||
} if (socks[0])
|
||||
|
||||
end while true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -140,3 +140,10 @@ protected
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Globalized socket constants
|
||||
#
|
||||
SHUT_RDWR = ::Socket::SHUT_RDWR
|
||||
SHUT_RD = ::Socket::SHUT_RD
|
||||
SHUT_WR = ::Socket::SHUT_WR
|
||||
|
@ -26,8 +26,34 @@ module ThreadSafe
|
||||
left = t
|
||||
|
||||
begin
|
||||
orig_size = rfd.length if (rfd)
|
||||
|
||||
# Poll the set supplied to us at least once.
|
||||
rv = ::IO.select(rfd, wfd, efd, DefaultCycle)
|
||||
begin
|
||||
rv = ::IO.select(rfd, wfd, efd, DefaultCycle)
|
||||
rescue IOError
|
||||
# If a stream was detected as being closed, re-raise the error as
|
||||
# a StreamClosedError with the specific file descriptor that was
|
||||
# detected as being closed. This is to better handle the case of
|
||||
# a closed socket being detected so that it can be cleaned up and
|
||||
# removed.
|
||||
if (rfd)
|
||||
rfd.each { |fd|
|
||||
raise StreamClosedError.new(fd) if (fd.closed?)
|
||||
}
|
||||
end
|
||||
|
||||
# If the original rfd length is not the same as the current
|
||||
# length, then the list may have been altered and as such may not
|
||||
# contain the socket that caused the IOError. This is a bad way
|
||||
# to do this since it's possible that the array length could be
|
||||
# back to the size that it was originally and yet have had the
|
||||
# socket that caused the IOError to be removed.
|
||||
return nil if (rfd and rfd.length != orig_size)
|
||||
|
||||
# Re-raise the exception since we didn't handle it here.
|
||||
raise $!
|
||||
end
|
||||
|
||||
return rv if (rv)
|
||||
|
||||
|
@ -6,6 +6,9 @@ $:.unshift(File.join(File.dirname(__FILE__), '../lib'))
|
||||
require 'rex'
|
||||
require 'msf/ui'
|
||||
|
||||
register_log_source('core', Rex::Logging::Sinks::Flatfile.new('/tmp/msfcli.log'))
|
||||
# TODO: streamline logging
|
||||
f = Rex::Logging::Sinks::Flatfile.new('/tmp/msfcli.log')
|
||||
register_log_source('rex', f)
|
||||
register_log_source('core', f)
|
||||
|
||||
Msf::Ui::Console::Driver.new.run
|
||||
|
Loading…
Reference in New Issue
Block a user