Land #7246 Rex::Socket Gemification

MS-1715
bug/bundler_fix
dmohanty-r7 2016-09-07 13:14:45 -05:00
commit 24bb6b18ea
No known key found for this signature in database
GPG Key ID: D1B3982EAB38742F
39 changed files with 27 additions and 5807 deletions

View File

@ -35,12 +35,14 @@ PATH
redcarpet
rex-arch
rex-bin_tools
rex-core
rex-java
rex-mime
rex-ole
rex-powershell
rex-random_identifier
rex-registry
rex-socket
rex-struct2
rex-text
rex-zip
@ -85,7 +87,7 @@ GEM
arel (6.0.3)
arel-helpers (2.3.0)
activerecord (>= 3.1.0, < 6)
aruba (0.14.1)
aruba (0.14.2)
childprocess (~> 0.5.6)
contracts (~> 0.9)
cucumber (>= 1.3.19)
@ -95,7 +97,7 @@ GEM
bcrypt (3.1.11)
bit-struct (0.15.0)
builder (3.2.2)
capybara (2.7.1)
capybara (2.8.1)
addressable
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@ -237,7 +239,7 @@ GEM
rex-core
rex-struct2
rex-text
rex-core (0.1.1)
rex-core (0.1.2)
rex-java (0.1.2)
rex-mime (0.1.1)
rex-text
@ -249,6 +251,8 @@ GEM
rex-random_identifier (0.1.0)
rex-text
rex-registry (0.1.0)
rex-socket (0.1.0)
rex-core
rex-struct2 (0.1.0)
rex-text (0.2.1)
rex-zip (0.1.0)
@ -263,7 +267,7 @@ GEM
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-rails (3.5.1)
rspec-rails (3.5.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)

View File

@ -3,7 +3,7 @@ require 'rex/io/stream_abstraction'
require 'rex/sync/ref'
require 'rex/payloads/meterpreter/uri_checksum'
require 'rex/post/meterpreter'
require 'rex/parser/x509_certificate'
require 'rex/socket/x509_certificate'
require 'msf/core/payload/windows/verify_ssl'
require 'rex/user_agent'

View File

@ -1,7 +1,7 @@
# -*- coding: binary -*-
require 'msf/core'
require 'rex/parser/x509_certificate'
require 'rex/socket/x509_certificate'
module Msf
@ -25,7 +25,7 @@ module Payload::Windows::VerifySsl
raise ArgumentError, "Verifying SSL cert is enabled but no handler cert is configured"
end
hash = Rex::Parser::X509Certificate.get_cert_file_hash(handler_cert)
hash = Rex::Socket::X509Certificate.get_cert_file_hash(handler_cert)
print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}")
hash
end

View File

@ -1,4 +1,5 @@
# -*- coding: binary -*-
require 'rex/ui/text/bidirectional_pipe'
module Msf
module Ui
module Web
@ -83,7 +84,7 @@ module Comm
end
def self.create_session_pipe(session)
pipe = Rex::IO::BidirectionalPipe.new
pipe = Rex::Ui::BidirectionalPipe.new
@session_pipes[session.id] = pipe

View File

@ -1,4 +1,5 @@
# -*- coding: binary -*-
require 'rex/ui/text/bidirectional_pipe'
module Msf
module Ui
module Web
@ -18,7 +19,7 @@ class WebConsole
attr_accessor :thread
# Wrapper class in case we need to extend the pipe
class WebConsolePipe < Rex::IO::BidirectionalPipe
class WebConsolePipe < Rex::Ui::Text::BidirectionalPipe
def prompting?
false
end

View File

@ -8,7 +8,7 @@ module Msf
module Ui
module Web
require 'rex/io/bidirectional_pipe'
require 'rex/ui/text/bidirectional_pipe'
require 'msf/ui/web/console'

View File

@ -1,26 +0,0 @@
# -*- coding: binary -*-
require 'rex/io/socket_abstraction'
module Rex
module IO
###
#
# This class provides an abstraction to a datagram based
# connection through the use of a datagram socketpair.
#
###
module DatagramAbstraction
include Rex::IO::SocketAbstraction
#
# Creates a streaming socket pair
#
def initialize_abstraction
self.lsock, self.rsock = Rex::Socket.udp_socket_pair
end
end
end; end

View File

@ -1,369 +0,0 @@
# -*- coding: binary -*-
#
# This class implements a ring buffer with "cursors" in the form of sequence numbers.
# To use this class, pass in a file descriptor and a ring size, the class will read
# data from the file descriptor and store it in the ring. If the ring becomes full,
# the oldest item will be overwritten. To emulate a stream interface, call read_data
# to grab the last sequence number and any buffered data, call read_data again,
# passing in the sequence number and all data newer than that sequence will be
# returned, along with a new sequence to read from.
#
require 'rex/socket'
module Rex
module IO
class RingBuffer
attr_accessor :queue # The data queue, essentially an array of two-element arrays, containing a sequence and data buffer
attr_accessor :seq # The next available sequence number
attr_accessor :fd # The associated socket or IO object for this ring buffer
attr_accessor :size # The number of available slots in the queue
attr_accessor :mutex # The mutex locking access to the queue
attr_accessor :beg # The index of the earliest data fragment in the ring
attr_accessor :cur # The sequence number of the earliest data fragment in the ring
attr_accessor :monitor # The thread handle of the built-in monitor when used
attr_accessor :monitor_thread_error # :nodoc: #
#
# Create a new ring buffer
#
def initialize(socket, opts={})
self.size = opts[:size] || (1024 * 4)
self.fd = socket
self.seq = 0
self.beg = 0
self.cur = 0
self.queue = Array.new( self.size )
self.mutex = Mutex.new
end
def inspect
"#<Rex::IO::RingBuffer @size=#{size} @fd=#{fd} @seq=#{seq} @beg=#{beg} @cur=#{cur}>"
end
#
# Start the built-in monitor, not called when used in a larger framework
#
def start_monitor
self.monitor = monitor_thread if not self.monitor
end
#
# Stop the built-in monitor
#
def stop_monitor
self.monitor.kill if self.monitor
self.monitor = nil
end
#
# The built-in monitor thread (normally unused with Metasploit)
#
def monitor_thread
Thread.new do
begin
while self.fd
buff = self.fd.get_once(-1, 1.0)
next if not buff
store_data(buff)
end
rescue ::Exception => e
self.monitor_thread_error = e
end
end
end
#
# Push data back into the associated stream socket. Logging must occur
# elsewhere, this function is simply a passthrough.
#
def put(data, opts={})
self.fd.put(data, opts={})
end
#
# The clear_data method wipes the ring buffer
#
def clear_data
self.mutex.synchronize do
self.seq = 0
self.beg = 0
self.cur = 0
self.queue = Array.new( self.size )
end
end
#
# The store_data method is used to insert data into the ring buffer.
#
def store_data(data)
self.mutex.synchronize do
# self.cur points to the array index of queue containing the last item
# adding data will result in cur + 1 being used to store said data
# if cur is larger than size - 1, it will wrap back around. If cur
# is *smaller* beg, beg is increemnted to cur + 1 (and wrapped if
# necessary
loc = 0
if self.seq > 0
loc = ( self.cur + 1 ) % self.size
if loc <= self.beg
self.beg = (self.beg + 1) % self.size
end
end
self.queue[loc] = [self.seq += 1, data]
self.cur = loc
end
end
#
# The read_data method returns a two element array with the new reader cursor (a sequence number)
# and the returned data buffer (if any). A result of nil/nil indicates that no data is available
#
def read_data(ptr=nil)
self.mutex.synchronize do
# Verify that there is data in the queue
return [nil,nil] if not self.queue[self.beg]
# Configure the beginning read pointer (sequence number, not index)
ptr ||= self.queue[self.beg][0]
return [nil,nil] if not ptr
# If the pointer is below our baseline, we lost some data, so jump forward
if ptr < self.queue[self.beg][0]
ptr = self.queue[self.beg][0]
end
# Calculate how many blocks exist between the current sequence number
# and the requested pointer, this becomes the number of blocks we will
# need to read to satisfy the result. Due to the mutex block, we do
# not need to scan to find the sequence of the starting block or
# check the sequence of the ending block.
dis = self.seq - ptr
# If the requested sequnce number is less than our base pointer, it means
# that no new data is available and we should return empty.
return [nil,nil] if dis < 0
# Calculate the beginning block index and number of blocks to read
off = ptr - self.queue[self.beg][0]
set = (self.beg + off) % self.size
# Build the buffer by reading forward by the number of blocks needed
# and return the last read sequence number, plus one, as the new read
# pointer.
buff = ""
cnt = 0
lst = ptr
ptr.upto(self.seq) do |i|
block = self.queue[ (set + cnt) % self.size ]
lst,data = block[0],block[1]
buff += data
cnt += 1
end
return [lst + 1, buff]
end
end
#
# The base_sequence method returns the earliest sequence number in the queue. This is zero until
# all slots are filled and the ring rotates.
#
def base_sequence
self.mutex.synchronize do
return 0 if not self.queue[self.beg]
return self.queue[self.beg][0]
end
end
#
# The last_sequence method returns the "next" sequence number where new data will be
# available.
#
def last_sequence
self.seq
end
#
# The create_steam method assigns a IO::Socket compatible object to the ringer buffer
#
def create_stream
Stream.new(self)
end
#
# The select method returns when there is a chance of new data
# XXX: This is mostly useless and requires a rewrite to use a
# real select or notify mechanism
#
def select
::IO.select([ self.fd ], nil, [ self.fd ], 0.10)
end
#
# The wait method blocks until new data is available
#
def wait(seq)
nseq = nil
while not nseq
nseq,data = read_data(seq)
select
end
end
#
# The wait_for method blocks until new data is available or the timeout is reached
#
def wait_for(seq,timeout=1)
begin
::Timeout.timeout(timeout) do
wait(seq)
end
rescue ::Timeout::Error
end
end
#
# This class provides a backwards compatible "stream" socket that uses
# the parents ring buffer.
#
class Stream
attr_accessor :ring
attr_accessor :seq
attr_accessor :buff
def initialize(ring)
self.ring = ring
self.seq = ring.base_sequence
self.buff = ''
end
def read(len=nil)
if len and self.buff.length >= len
data = self.buff.slice!(0,len)
return data
end
while true
lseq, data = self.ring.read_data( self.seq )
return if not lseq
self.seq = lseq
self.buff << data
if len
if self.buff.length >= len
return self.buff.slice!(0,len)
else
IO.select(nil, nil, nil, 0.25)
next
end
end
data = self.buff
self.buff = ''
return data
# Not reached
break
end
end
def write(data)
self.ring.write(data)
end
end
end
end
end
=begin
server = Rex::Socket.create_tcp_server('LocalPort' => 0)
lport = server.getsockname[2]
client = Rex::Socket.create_tcp('PeerHost' => '127.0.0.1', 'PeerPort' => lport)
conn = server.accept
r = Rex::IO::RingBuffer.new(conn, {:size => 1024*1024})
client.put("1")
client.put("2")
client.put("3")
s,d = r.read_data
client.put("4")
client.put("5")
client.put("6")
s,d = r.read_data(s)
client.put("7")
client.put("8")
client.put("9")
s,d = r.read_data(s)
client.put("0")
s,d = r.read_data(s)
test_counter = 11
1.upto(100) do
client.put( "X" )
test_counter += 1
end
sleep(1)
s,d = r.read_data
p s
p d
fdata = ''
File.open("/bin/ls", "rb") do |fd|
fdata = fd.read(fd.stat.size)
fdata = fdata * 10
client.put(fdata)
end
sleep(1)
s,vdata = r.read_data(s)
if vdata != fdata
puts "DATA FAILED"
else
puts "DATA VERIFIED"
end
r.clear_data
a = r.create_stream
b = r.create_stream
client.put("ABC123")
sleep(1)
p a.read
p b.read
client.put("$$$$$$")
sleep(1)
p a.read
p b.read
c = r.create_stream
p c.read
=end

View File

@ -1,205 +0,0 @@
# -*- coding: binary -*-
require 'socket'
require 'fcntl'
module Rex
module IO
###
#
# This class provides an abstraction to a stream based
# connection through the use of a streaming socketpair.
#
###
module SocketAbstraction
###
#
# Extension information for required Stream interface.
#
###
module Ext
#
# Initializes peer information.
#
def initinfo(peer,local)
@peer = peer
@local = local
end
#
# Symbolic peer information.
#
def peerinfo
(@peer || "Remote Pipe")
end
#
# Symbolic local information.
#
def localinfo
(@local || "Local Pipe")
end
end
#
# Override this method to init the abstraction
#
def initialize_abstraction
self.lsock, self.rsock = Rex::Compat.pipe
end
#
# This method cleans up the abstraction layer.
#
def cleanup_abstraction
self.lsock.close if (self.lsock and !self.lsock.closed?)
self.rsock.close if (self.rsock and !self.rsock.closed?)
self.lsock = nil
self.rsock = nil
end
#
# Low-level write to the local side.
#
def syswrite(buffer)
lsock.syswrite(buffer)
end
#
# Low-level read from the local side.
#
def sysread(length)
lsock.sysread(length)
end
#
# Shuts down the local side of the stream abstraction.
#
def shutdown(how)
lsock.shutdown(how)
end
#
# Closes both sides of the stream abstraction.
#
def close
cleanup_abstraction
super
end
#
# Symbolic peer information.
#
def peerinfo
"Remote-side of Pipe"
end
#
# Symbolic local information.
#
def localinfo
"Local-side of Pipe"
end
#
# The left side of the stream.
#
attr_reader :lsock
#
# The right side of the stream.
#
attr_reader :rsock
protected
def monitor_rsock(threadname = "SocketMonitorRemote")
self.monitor_thread = Rex::ThreadFactory.spawn(threadname, false) {
loop do
closed = false
buf = nil
if not self.rsock
wlog("monitor_rsock: the remote socket is nil, exiting loop")
break
end
begin
s = Rex::ThreadSafe.select( [ self.rsock ], nil, nil, 0.2 )
if( s == nil || s[0] == nil )
next
end
rescue Exception => e
wlog("monitor_rsock: exception during select: #{e.class} #{e}")
closed = true
end
if( closed == false )
begin
buf = self.rsock.sysread( 32768 )
if buf == nil
closed = true
wlog("monitor_rsock: closed remote socket due to nil read")
end
rescue EOFError => e
closed = true
dlog("monitor_rsock: EOF in rsock")
rescue ::Exception => e
closed = true
wlog("monitor_rsock: exception during read: #{e.class} #{e}")
end
end
if( closed == false )
total_sent = 0
total_length = buf.length
while( total_sent < total_length )
begin
data = buf[total_sent, buf.length]
# Note that this must be write() NOT syswrite() or put() or anything like it.
# Using syswrite() breaks SSL streams.
sent = self.write( data )
# sf: Only remove the data off the queue is write was successfull.
# This way we naturally perform a resend if a failure occured.
# Catches an edge case with meterpreter TCP channels where remote send
# failes gracefully and a resend is required.
if (sent.nil?)
closed = true
wlog("monitor_rsock: failed writing, socket must be dead")
break
elsif (sent > 0)
total_sent += sent
end
rescue ::IOError, ::EOFError => e
closed = true
wlog("monitor_rsock: exception during write: #{e.class} #{e}")
break
end
end
end
if( closed )
begin
self.close_write if self.respond_to?('close_write')
rescue IOError
end
break
end
end
}
end
protected
attr_accessor :monitor_thread
attr_writer :lsock
attr_writer :rsock
end
end; end

View File

@ -1,312 +0,0 @@
# -*- coding: binary -*-
require 'rex/sync/thread_safe'
module Rex
module IO
###
#
# This mixin is an abstract representation of a streaming connection. Streams
# extend classes that must implement the following methods:
#
# syswrite(buffer)
# sysread(length)
# shutdown(how)
# close
# peerinfo
# localinfo
#
###
module Stream
##
#
# Abstract methods
#
##
#
# This method writes the supplied buffer to the stream. This method
# intelligent reduces the size of supplied buffers so that ruby doesn't get
# into a potential global thread blocking state when used on blocking
# sockets. That is, this method will send the supplied buffer in chunks
# of, at most, 32768 bytes.
#
def write(buf, opts = {})
total_sent = 0
total_length = buf.length
block_size = 32768
begin
while( total_sent < total_length )
s = Rex::ThreadSafe.select( nil, [ fd ], nil, 0.2 )
if( s == nil || s[0] == nil )
next
end
data = buf[total_sent, block_size]
sent = fd.write_nonblock( data )
if sent > 0
total_sent += sent
end
end
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
# Sleep for a half a second, or until we can write again
Rex::ThreadSafe.select( nil, [ fd ], nil, 0.5 )
# Decrement the block size to handle full sendQs better
block_size = 1024
# Try to write the data again
retry
rescue ::IOError, ::Errno::EPIPE
return nil
end
total_sent
end
#
# This method reads data of the supplied length from the stream.
#
def read(length = nil, opts = {})
begin
return fd.read_nonblock( length )
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
# Sleep for a half a second, or until we can read again
Rex::ThreadSafe.select( [ fd ], nil, nil, 0.5 )
# Decrement the block size to handle full sendQs better
retry
rescue ::IOError, ::Errno::EPIPE
return nil
end
end
#
# Polls the stream to see if there is any read data available. Returns
# true if data is available for reading, otherwise false is returned.
#
def has_read_data?(timeout = nil)
# Allow a timeout of "0" that waits almost indefinitely for input, this
# mimics the behavior of Rex::ThreadSafe.select() and fixes some corner
# cases of unintentional no-wait timeouts.
timeout = 3600 if (timeout and timeout == 0)
begin
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
(rv[0]) and
(rv[0][0] == fd))
true
else
false
end
rescue ::Errno::EBADF, ::Errno::ENOTSOCK
raise ::EOFError
rescue StreamClosedError, ::IOError, ::EOFError, ::Errno::EPIPE
# Return false if the socket is dead
return false
end
end
#
# This method returns the selectable file descriptor, or self by default.
#
def fd
self
end
##
#
# Common methods
#
##
#
# This method writes the supplied buffer to the stream by calling the write
# routine.
#
def <<(buf)
return write(buf.to_s)
end
#
# This method calls get_once() to read pending data from the socket
#
def >>
get_once
end
#
# This method writes to the stream, optionally timing out after a period of
# time.
#
def timed_write(buf, wait = def_write_timeout, opts = {})
if (wait and wait > 0)
Timeout.timeout(wait) {
return write(buf, opts)
}
else
return write(buf, opts)
end
end
#
# This method reads from the stream, optionally timing out after a period
# of time.
#
def timed_read(length = nil, wait = def_read_timeout, opts = {})
if (wait and wait > 0)
Timeout.timeout(wait) {
return read(length, opts)
}
else
return read(length, opts)
end
end
#
# This method writes the full contents of the supplied buffer, optionally
# with a timeout.
#
def put(buf, opts = {})
return 0 if (buf == nil or buf.length == 0)
send_len = buf.length
send_idx = 0
wait = opts['Timeout'] || 0
# Keep writing until our send length drops to zero
while (send_idx < send_len)
curr_len = timed_write(buf[send_idx, buf.length-send_idx], wait, opts)
# If the write operation failed due to an IOError, then we fail.
return buf.length - send_len if (curr_len == nil)
send_len -= curr_len
send_idx += curr_len
end
return buf.length - send_len
end
#
# This method emulates the behavior of Pex::Socket::Recv in MSF2
#
def get_once(length = -1, timeout = def_read_timeout)
if (has_read_data?(timeout) == false)
return nil
end
bsize = (length == -1) ? def_block_size : length
data = read(bsize)
raise EOFError if data.nil?
data
end
#
# This method reads as much data as it can from the wire given a maximum
# timeout.
#
def get(timeout = nil, ltimeout = def_read_loop_timeout, opts = {})
# For those people who are used to being able to use a negative timeout!
if (timeout and timeout.to_i < 0)
timeout = nil
end
# No data in the first place? bust.
if (has_read_data?(timeout) == false)
return nil
end
buf = ""
lps = 0
eof = false
# Keep looping until there is no more data to be gotten..
while (has_read_data?(ltimeout) == true)
# Catch EOF errors so that we can handle them properly.
begin
temp = read(def_block_size)
rescue EOFError
eof = true
end
# If we read zero bytes and we had data, then we've hit EOF
if (temp and temp.length == 0)
eof = true
end
# If we reached EOF and there are no bytes in the buffer we've been
# reading into, then throw an EOF error.
if (eof)
# If we've already read at least some data, then it's time to
# break out and let it be processed before throwing an EOFError.
if (buf.length > 0)
break
else
raise EOFError
end
end
break if (temp == nil or temp.empty? == true)
buf += temp
lps += 1
break if (lps >= def_max_loops)
end
# Return the entire buffer we read in
return buf
end
##
#
# Defaults
#
##
#
# The default number of seconds to wait for a write operation to timeout.
#
def def_write_timeout
10
end
#
# The default number of seconds to wait for a read operation to timeout.
#
def def_read_timeout
10
end
#
# The default number of seconds to wait while in a read loop after read
# data has been found.
#
def def_read_loop_timeout
0.1
end
#
# The maximum number of read loops to perform before returning to the
# caller.
#
def def_max_loops
1024
end
#
# The default block size to read in chunks from the wire.
#
def def_block_size
16384
end
protected
end
end end

View File

@ -1,32 +0,0 @@
# -*- coding: binary -*-
require 'rex/io/socket_abstraction'
module Rex
module IO
###
#
# This class provides an abstraction to a stream based
# connection through the use of a streaming socketpair.
#
###
module StreamAbstraction
include Rex::IO::SocketAbstraction
#
# This method creates a streaming socket pair and initializes it.
#
def initialize_abstraction
self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
self.lsock.extend(Rex::IO::Stream)
self.lsock.extend(Ext)
self.rsock.extend(Rex::IO::Stream)
self.monitor_rsock("StreamMonitorRemote")
end
end
end; end

View File

@ -1,221 +0,0 @@
# -*- coding: binary -*-
require 'thread'
module Rex
module IO
###
#
# This mixin provides the framework and interface for implementing a streaming
# server that can listen for and accept stream client connections. Stream
# servers extend this class and are required to implement the following
# methods:
#
# accept
# fd
#
###
module StreamServer
##
#
# Abstract methods
#
##
##
#
# Default server monitoring and client management implementation follows
# below.
#
##
#
# This callback is notified when a client connects.
#
def on_client_connect(client)
if (on_client_connect_proc)
on_client_connect_proc.call(client)
end
end
#
# This callback is notified when a client connection has data that needs to
# be processed.
#
def on_client_data(client)
if (on_client_data_proc)
on_client_data_proc.call(client)
end
end
#
# This callback is notified when a client connection has closed.
#
def on_client_close(client)
if (on_client_close_proc)
on_client_close_proc.call(client)
end
end
#
# Start monitoring the listener socket for connections and keep track of
# all client connections.
#
def start
self.clients = []
self.client_waiter = ::Queue.new
self.listener_thread = Rex::ThreadFactory.spawn("StreamServerListener", false) {
monitor_listener
}
self.clients_thread = Rex::ThreadFactory.spawn("StreamServerClientMonitor", false) {
monitor_clients
}
end
#
# Terminates the listener monitoring threads and closes all active clients.
#
def stop
self.listener_thread.kill
self.clients_thread.kill
self.clients.each { |cli|
close_client(cli)
}
end
#
# This method closes a client connection and cleans up the resources
# associated with it.
#
def close_client(client)
if (client)
clients.delete(client)
begin
client.close
rescue IOError
end
end
end
#
# This method waits on the server listener thread
#
def wait
self.listener_thread.join if self.listener_thread
end
##
#
# Callback procedures.
#
##
#
# This callback procedure can be set and will be called when new clients
# connect.
#
attr_accessor :on_client_connect_proc
#
# This callback procedure can be set and will be called when clients
# have data to be processed.
#
attr_accessor :on_client_data_proc
#
# This callback procedure can be set and will be called when a client
# disconnects from the server.
#
attr_accessor :on_client_close_proc
attr_accessor :clients # :nodoc:
attr_accessor :listener_thread, :clients_thread # :nodoc:
attr_accessor :client_waiter
protected
#
# This method monitors the listener socket for new connections and calls
# the +on_client_connect+ callback routine.
#
def monitor_listener
while true
begin
cli = accept
if not cli
elog("The accept() returned nil in stream server listener monitor: #{fd.inspect}")
::IO.select(nil, nil, nil, 0.10)
next
end
# Append to the list of clients
self.clients << cli
# Initialize the connection processing
on_client_connect(cli)
# Notify the client monitor
self.client_waiter.push(cli)
# Skip exceptions caused by accept() [ SSL ]
rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
rescue ::Interrupt
raise $!
rescue ::Exception
elog("Error in stream server server monitor: #{$!}")
rlog(ExceptionCallStack)
break
end
end
end
#
# This method monitors client connections for data and calls the
# +on_client_data+ routine when new data arrives.
#
def monitor_clients
begin
# Wait for a notify if our client list is empty
if (clients.length == 0)
self.client_waiter.pop
next
end
sd = Rex::ThreadSafe.select(clients, nil, nil, nil)
sd[0].each { |cfd|
begin
on_client_data(cfd)
rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
on_client_close(cfd)
close_client(cfd)
rescue ::Interrupt
raise $!
rescue ::Exception
close_client(cfd)
elog("Error in stream server client monitor: #{$!}")
rlog(ExceptionCallStack)
end
}
rescue ::Rex::StreamClosedError => e
# Remove the closed stream from the list
clients.delete(e.stream)
rescue ::Interrupt
raise $!
rescue ::Exception
elog("Error in stream server client monitor: #{$!}")
rlog(ExceptionCallStack)
end while true
end
end
end
end

View File

@ -1,92 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
module Rex
module Parser
###
#
# This class parses the contents of a PEM-encoded X509 certificate file containing
# a private key, a public key, and any appended glue certificates.
#
###
class X509Certificate
#
# Parse a certificate in unified PEM format that contains a private key and
# one or more certificates. The first certificate is the primary, while any
# additional certificates are treated as intermediary certificates. This emulates
# the behavior of web servers like nginx.
#
# @param [String] ssl_cert
# @return [String, String, Array]
def self.parse_pem(ssl_cert)
cert = nil
key = nil
chain = nil
certs = []
ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem|
if pem =~ /PRIVATE KEY/
key = OpenSSL::PKey::RSA.new(pem)
elsif pem =~ /CERTIFICATE/
certs << OpenSSL::X509::Certificate.new(pem)
end
end
cert = certs.shift
if certs.length > 0
chain = certs
end
[key, cert, chain]
end
#
# Parse a certificate in unified PEM format from a file
#
# @param [String] ssl_cert_file
# @return [String, String, Array]
def self.parse_pem_file(ssl_cert_file)
data = ''
::File.open(ssl_cert_file, 'rb') do |fd|
data << fd.read(fd.stat.size)
end
parse_pem(data)
end
#
# Parse a certificate in unified PEM format and retrieve
# the SHA1 hash.
#
# @param [String] ssl_cert
# @return [String]
def self.get_cert_hash(ssl_cert)
hcert = parse_pem(ssl_cert)
unless hcert and hcert[0] and hcert[1]
raise ArgumentError, "Could not parse a private key and certificate"
end
Rex::Text.sha1_raw(hcert[1].to_der)
end
#
# Parse a file that contains a certificate in unified PEM
# format and retrieve the SHA1 hash.
#
# @param [String] ssl_cert_file
# @return [String]
def self.get_cert_file_hash(ssl_cert_file)
data = ''
::File.open(ssl_cert_file, 'rb') do |fd|
data << fd.read(fd.stat.size)
end
get_cert_hash(data)
end
end
end
end

View File

@ -2,7 +2,7 @@
require 'msf/core/payload/uuid'
require 'msf/core/payload/windows'
require 'msf/core/reflective_dll_loader'
require 'rex/parser/x509_certificate'
require 'rex/socket/x509_certificate'
class Rex::Payloads::Meterpreter::Config

View File

@ -13,7 +13,7 @@ require 'msf/core/payload/uuid'
require 'rex/payloads/meterpreter/uri_checksum'
# certificate hash checking
require 'rex/parser/x509_certificate'
require 'rex/socket/x509_certificate'
module Rex
module Post
@ -686,7 +686,7 @@ class ClientCore < Extension
request.add_tlv(TLV_TYPE_TRANS_UA, opts[:ua])
if transport == METERPRETER_TRANSPORT_HTTPS && opts[:cert]
hash = Rex::Parser::X509Certificate.get_cert_file_hash(opts[:cert])
hash = Rex::Socket::X509Certificate.get_cert_file_hash(opts[:cert])
request.add_tlv(TLV_TYPE_TRANS_CERT_HASH, hash)
end

View File

@ -1,796 +0,0 @@
# -*- coding: binary -*-
require 'socket'
require 'thread'
require 'resolv'
require 'rex/exceptions'
module Rex
###
#
# Base class for all sockets.
#
###
module Socket
module Comm
end
require 'rex/socket/parameters'
require 'rex/socket/tcp'
require 'rex/socket/tcp_server'
require 'rex/socket/comm'
require 'rex/socket/comm/local'
require 'rex/socket/switch_board'
require 'rex/socket/subnet_walker'
require 'rex/socket/range_walker'
##
#
# Factory methods
#
##
#
# Create a socket instance using the supplied parameter hash.
#
def self.create(opts = {})
return create_param(Rex::Socket::Parameters.from_hash(opts))
end
#
# Create a socket using the supplied Rex::Socket::Parameter instance.
#
def self.create_param(param)
return param.comm.create(param)
end
#
# Create a TCP socket using the supplied parameter hash.
#
def self.create_tcp(opts = {})
return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'tcp')))
end
#
# Create a TCP server socket using the supplied parameter hash.
#
def self.create_tcp_server(opts = {})
return create_tcp(opts.merge('Server' => true))
end
#
# Create a UDP socket using the supplied parameter hash.
#
def self.create_udp(opts = {})
return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'udp')))
end
#
# Create a IP socket using the supplied parameter hash.
#
def self.create_ip(opts = {})
return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'ip')))
end
#
# Common Regular Expressions
#
MATCH_IPV6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
MATCH_IPV4 = /^\s*(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))\s*$/
MATCH_IPV4_PRIVATE = /^\s*(?:10\.|192\.168|172.(?:1[6-9]|2[0-9]|3[01])\.|169\.254)/
##
#
# Serialization
#
##
# Cache our IPv6 support flag
@@support_ipv6 = nil
#
# Determine whether we support IPv6
#
def self.support_ipv6?
return @@support_ipv6 if not @@support_ipv6.nil?
@@support_ipv6 = false
if (::Socket.const_defined?('AF_INET6'))
begin
s = ::Socket.new(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
s.close
@@support_ipv6 = true
rescue
end
end
return @@support_ipv6
end
#
# Determine whether this is an IPv4 address
#
def self.is_ipv4?(addr)
( addr =~ MATCH_IPV4 ) ? true : false
end
#
# Determine whether this is an IPv6 address
#
def self.is_ipv6?(addr)
( addr =~ MATCH_IPV6 ) ? true : false
end
#
# Checks to see if the supplied address is in "dotted" form
#
def self.dotted_ip?(addr)
# Match IPv6
return true if (support_ipv6? and addr =~ MATCH_IPV6)
# Match IPv4
return true if (addr =~ MATCH_IPV4)
false
end
#
# Return true if +addr+ is within the ranges specified in RFC1918, or
# RFC5735/RFC3927
#
def self.is_internal?(addr)
if self.dotted_ip?(addr)
addr =~ MATCH_IPV4_PRIVATE
else
false
end
end
# Get the first address returned by a DNS lookup for +hostname+.
#
# @see .getaddresses
#
# @param (see .getaddresses)
# @return [String] ASCII IP address
def self.getaddress(hostname, accept_ipv6 = true)
getaddresses(hostname, accept_ipv6).first
end
#
# Wrapper for +::Socket.gethostbyname+ that takes special care to see if the
# supplied address is already an ASCII IP address. This is necessary to
# prevent blocking while waiting on a DNS reverse lookup when we already
# have what we need.
#
# @param hostname [String] A hostname or ASCII IP address
# @return [Array<String>]
def self.getaddresses(hostname, accept_ipv6 = true)
if hostname =~ MATCH_IPV4 or (accept_ipv6 and hostname =~ MATCH_IPV6)
return [hostname]
end
res = ::Socket.gethostbyname(hostname)
return [] if not res
# Shift the first three elements out, leaving just the list of
# addresses
res.shift # name
res.shift # alias hostnames
res.shift # address_family
# Rubinius has a bug where gethostbyname returns dotted quads instead of
# NBO, but that's what we want anyway, so just short-circuit here.
if res[0] =~ MATCH_IPV4 || res[0] =~ MATCH_IPV6
unless accept_ipv6
res.reject!{ |ascii| ascii =~ MATCH_IPV6 }
end
else
unless accept_ipv6
res.reject!{ |nbo| nbo.length != 4 }
end
res.map!{ |nbo| self.addr_ntoa(nbo) }
end
res
end
#
# Wrapper for Socket.gethostbyname which takes into account whether or not
# an IP address is supplied. If it is, then reverse DNS resolution does
# not occur. This is done in order to prevent delays, such as would occur
# on Windows.
#
def self.gethostbyname(host)
if (is_ipv4?(host))
return [ host, [], 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
end
if is_ipv6?(host)
# pop off the scopeid since gethostbyname isn't smart enough to
# deal with it.
host, _ = host.split('%', 2)
end
::Socket.gethostbyname(host)
end
#
# Create a sockaddr structure using the supplied IP address, port, and
# address family
#
def self.to_sockaddr(ip, port)
if (ip == '::ffff:0.0.0.0')
ip = support_ipv6?() ? '::' : '0.0.0.0'
end
return ::Socket.pack_sockaddr_in(port, ip)
end
#
# Returns the address family, host, and port of the supplied sockaddr as
# [ af, host, port ]
#
def self.from_sockaddr(saddr)
port, host = ::Socket::unpack_sockaddr_in(saddr)
af = ::Socket::AF_INET
if (support_ipv6?() and is_ipv6?(host))
af = ::Socket::AF_INET6
end
return [ af, host, port ]
end
#
# Resolves a host to raw network-byte order.
#
def self.resolv_nbo(host)
self.gethostbyname( Rex::Socket.getaddress(host, true) )[3]
end
#
# Resolves a host to raw network-byte order.
#
def self.resolv_nbo_list(host)
Rex::Socket.getaddresses(host).map{|addr| self.gethostbyname(addr)[3] }
end
#
# Resolves a host to a network-byte order ruby integer.
#
def self.resolv_nbo_i(host)
addr_ntoi(resolv_nbo(host))
end
#
# Resolves a host to a list of network-byte order ruby integers.
#
def self.resolv_nbo_i_list(host)
resolv_nbo_list(host).map{|addr| addr_ntoi(addr) }
end
#
# Converts an ASCII IP address to a CIDR mask. Returns
# nil if it's not convertable.
#
def self.addr_atoc(mask)
mask_i = resolv_nbo_i(mask)
cidr = nil
0.upto(32) do |i|
if ((1 << i)-1) << (32-i) == mask_i
cidr = i
break
end
end
return cidr
end
#
# Resolves a CIDR bitmask into a dotted-quad. Returns
# nil if it's not convertable.
#
def self.addr_ctoa(cidr)
return nil unless (0..32) === cidr.to_i
addr_itoa(((1 << cidr)-1) << 32-cidr)
end
#
# Resolves a host to a dotted address.
#
def self.resolv_to_dotted(host)
addr_ntoa(addr_aton(host))
end
#
# Converts a ascii address into an integer
#
def self.addr_atoi(addr)
resolv_nbo_i(addr)
end
#
# Converts a ascii address into a list of addresses
#
def self.addr_atoi_list(addr)
resolv_nbo_i_list(addr)
end
#
# Converts an integer address into ascii
#
# @param (see #addr_iton)
# @return (see #addr_ntoa)
def self.addr_itoa(addr, v6=false)
nboa = addr_iton(addr, v6)
addr_ntoa(nboa)
end
#
# Converts a ascii address to network byte order
#
def self.addr_aton(addr)
resolv_nbo(addr)
end
#
# Converts a network byte order address to ascii
#
# @param addr [String] Packed network-byte-order address
# @return [String] Human readable IP address.
def self.addr_ntoa(addr)
# IPv4
if (addr.length == 4)
return addr.unpack('C4').join('.')
end
# IPv6
if (addr.length == 16)
return compress_address(addr.unpack('n8').map{ |c| "%x" % c }.join(":"))
end
raise RuntimeError, "Invalid address format"
end
#
# Implement zero compression for IPv6 addresses.
# Uses the compression method from Marco Ceresa's IPAddress GEM
#
# @see https://github.com/bluemonk/ipaddress/blob/master/lib/ipaddress/ipv6.rb
#
# @param addr [String] Human readable IPv6 address
# @return [String] Human readable IPv6 address with runs of 0s removed
def self.compress_address(addr)
return addr unless is_ipv6?(addr)
addr = addr.dup
while true
break if addr.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
break if addr.sub!(/\b0:0:0:0:0:0:0\b/, ':')
break if addr.sub!(/\b0:0:0:0:0:0\b/, ':')
break if addr.sub!(/\b0:0:0:0:0\b/, ':')
break if addr.sub!(/\b0:0:0:0\b/, ':')
break if addr.sub!(/\b0:0:0\b/, ':')
break if addr.sub!(/\b0:0\b/, ':')
break
end
addr.sub(/:{3,}/, '::')
end
#
# Converts a network byte order address to an integer
#
def self.addr_ntoi(addr)
bits = addr.unpack("N*")
if (bits.length == 1)
return bits[0]
end
if (bits.length == 4)
val = 0
bits.each_index { |i| val += ( bits[i] << (96 - (i * 32)) ) }
return val
end
raise RuntimeError, "Invalid address format"
end
#
# Converts an integer into a network byte order address
#
# @param addr [Numeric] The address as a number
# @param v6 [Boolean] Whether +addr+ is IPv6
def self.addr_iton(addr, v6=false)
if(addr < 0x100000000 && !v6)
return [addr].pack('N')
else
w = []
w[0] = (addr >> 96) & 0xffffffff
w[1] = (addr >> 64) & 0xffffffff
w[2] = (addr >> 32) & 0xffffffff
w[3] = addr & 0xffffffff
return w.pack('N4')
end
end
#
# Converts a colon-delimited MAC address into a 6-byte binary string
#
def self.eth_aton(mac)
mac.split(":").map{|c| c.to_i(16) }.pack("C*")
end
#
# Converts a 6-byte binary string into a colon-delimited MAC address
#
def self.eth_ntoa(bin)
bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase
end
#
# Converts a CIDR subnet into an array (base, bcast)
#
def self.cidr_crack(cidr, v6=false)
tmp = cidr.split('/')
tst,scope = tmp[0].split("%",2)
scope = "%" + scope if scope
scope ||= ""
addr = addr_atoi(tst)
bits = 32
mask = 0
use6 = false
if (addr > 0xffffffff or v6 or cidr =~ /:/)
use6 = true
bits = 128
end
mask = (2 ** bits) - (2 ** (bits - tmp[1].to_i))
base = addr & mask
stop = base + (2 ** (bits - tmp[1].to_i)) - 1
return [self.addr_itoa(base, use6) + scope, self.addr_itoa(stop, use6) + scope]
end
#
# Converts a netmask (255.255.255.240) into a bitmask (28). This is the
# lame kid way of doing it.
#
def self.net2bitmask(netmask)
nmask = resolv_nbo(netmask)
imask = addr_ntoi(nmask)
bits = 32
if (imask > 0xffffffff)
bits = 128
end
0.upto(bits-1) do |bit|
p = 2 ** bit
return (bits - bit) if ((imask & p) == p)
end
0
end
#
# Converts a bitmask (28) into a netmask (255.255.255.240)
#
def self.bit2netmask(bitmask, ipv6=false)
if bitmask > 32 or ipv6
i = ((~((2 ** (128 - bitmask)) - 1)) & (2**128-1))
n = Rex::Socket.addr_iton(i, true)
return Rex::Socket.addr_ntoa(n)
else
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
end
end
def self.portspec_crack(pspec)
portspec_to_portlist(pspec)
end
#
# Converts a port specification like "80,21-25,!24,443" into a sorted,
# unique array of valid port numbers like [21,22,23,25,80,443]
#
def self.portspec_to_portlist(pspec)
ports = []
remove = []
# Build ports array from port specification
pspec.split(/,/).each do |item|
target = ports
item.strip!
if item.start_with? '!'
item.delete! '!'
target = remove
end
start, stop = item.split(/-/).map { |p| p.to_i }
start ||= 0
stop ||= item.match(/-/) ? 65535 : start
start, stop = stop, start if stop < start
start.upto(stop) { |p| target << p }
end
if ports.empty? and not remove.empty? then
ports = 1.upto 65535
end
# Sort, and remove dups and invalid ports
ports.sort.uniq.delete_if { |p| p < 1 or p > 65535 or remove.include? p }
end
#
# Converts a port list like [1,2,3,4,5,100] into a
# range specification like "1-5,100"
#
def self.portlist_to_portspec(parr)
ranges = []
range = []
lastp = nil
parr.uniq.sort{|a,b| a<=>b}.map{|a| a.to_i}.each do |n|
next if (n < 1 or n > 65535)
if not lastp
range = [n]
lastp = n
next
end
if lastp == n - 1
range << n
else
ranges << range
range = [n]
end
lastp = n
end
ranges << range
ranges.delete(nil)
ranges.uniq.map{|x| x.length == 1 ? "#{x[0]}" : "#{x[0]}-#{x[-1]}"}.join(",")
end
##
#
# Utility class methods
#
##
#
# This method does NOT send any traffic to the destination, instead, it uses a
# "bound" UDP socket to determine what source address we would use to
# communicate with the specified destination. The destination defaults to
# Google's DNS server to make the standard behavior determine which IP
# we would use to communicate with the internet.
#
def self.source_address(dest='8.8.8.8', comm = ::Rex::Socket::Comm::Local)
begin
s = self.create_udp(
'PeerHost' => dest,
'PeerPort' => 31337,
'Comm' => comm
)
r = s.getsockname[1]
s.close
# Trim off the trailing interface ID for link-local IPv6
return r.split('%').first
rescue ::Exception
return '127.0.0.1'
end
end
#
# Identifies the link-local address of a given interface (if IPv6 is enabled)
#
def self.ipv6_link_address(intf)
r = source_address("FF02::1%#{intf}")
return nil if r.nil? || r !~ /^fe80/i
r
end
#
# Identifies the mac address of a given interface (if IPv6 is enabled)
#
def self.ipv6_mac(intf)
r = ipv6_link_address(intf)
return if not r
raw = addr_aton(r)[-8, 8]
(raw[0,3] + raw[5,3]).unpack("C*").map{|c| "%.2x" % c}.join(":")
end
#
# Create a TCP socket pair.
#
# sf: This create a socket pair using native ruby sockets and will work
# on Windows where ::Socket.pair is not implemented.
# Note: OpenSSL requires native ruby sockets for its io.
#
# Note: Even though sub-threads are smashing the parent threads local, there
# is no concurrent use of the same locals and this is safe.
def self.tcp_socket_pair
lsock = nil
rsock = nil
laddr = '127.0.0.1'
lport = 0
threads = []
mutex = ::Mutex.new
threads << Rex::ThreadFactory.spawn('TcpSocketPair', false) {
server = nil
mutex.synchronize {
threads << Rex::ThreadFactory.spawn('TcpSocketPairClient', false) {
mutex.synchronize {
rsock = ::TCPSocket.new( laddr, lport )
}
}
server = ::TCPServer.new(laddr, 0)
if (server.getsockname =~ /127\.0\.0\.1:/)
# JRuby ridiculousness
caddr, lport = server.getsockname.split(":")
caddr = caddr[1,caddr.length]
lport = lport.to_i
else
# Sane implementations where Socket#getsockname returns a
# sockaddr
lport, caddr = ::Socket.unpack_sockaddr_in( server.getsockname )
end
}
lsock, _ = server.accept
server.close
}
threads.each { |t| t.join }
return [lsock, rsock]
end
#
# Create a UDP socket pair using native ruby UDP sockets.
#
def self.udp_socket_pair
laddr = '127.0.0.1'
lsock = ::UDPSocket.new
lsock.bind( laddr, 0 )
rsock = ::UDPSocket.new
rsock.bind( laddr, 0 )
rsock.connect( *lsock.addr.values_at(3,1) )
lsock.connect( *rsock.addr.values_at(3,1) )
return [lsock, rsock]
end
##
#
# Class initialization
#
##
#
# Initialize general socket parameters.
#
def initsock(params = nil)
if (params)
self.peerhost = params.peerhost
self.peerport = params.peerport
self.localhost = params.localhost
self.localport = params.localport
self.context = params.context || {}
self.ipv = params.v6 ? 6 : 4
end
end
#
# By default, all sockets are themselves selectable file descriptors.
#
def fd
self
end
#
# Returns local connection information.
#
def getsockname
Socket.from_sockaddr(super)
end
#
# Wrapper around getsockname
#
def getlocalname
getsockname
end
#
# Return peer connection information.
#
def getpeername_as_array
peer_name = nil
begin
peer_name = Socket.from_sockaddr(self.getpeername)
rescue ::Errno::EINVAL => e
# Ruby's getpeername method may call rb_sys_fail("getpeername(2)")
elog("#{e.message} (#{e.class})#{e.backtrace * "\n"}\n", 'core', LEV_3)
end
return peer_name
end
#
# Returns a string that indicates the type of the socket, such as 'tcp'.
#
def type?
raise NotImplementedError, "Socket type is not supported."
end
#
# The peer host of the connected socket.
#
attr_reader :peerhost
#
# The peer port of the connected socket.
#
attr_reader :peerport
#
# The local host of the connected socket.
#
attr_reader :localhost
#
# The local port of the connected socket.
#
attr_reader :localport
#
# The IP version of the socket
#
attr_reader :ipv
#
# Contextual information that describes the source and other
# instance-specific attributes. This comes from the param.context
# attribute.
#
attr_reader :context
protected
attr_writer :peerhost, :peerport, :localhost, :localport # :nodoc:
attr_writer :context # :nodoc:
attr_writer :ipv # :nodoc:
end
end
#
# Globalized socket constants
#
SHUT_RDWR = ::Socket::SHUT_RDWR
SHUT_RD = ::Socket::SHUT_RD
SHUT_WR = ::Socket::SHUT_WR

View File

@ -1,120 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
module Rex
module Socket
###
#
# This mixin provides the basic interface that a derived class must implement
# in order to be a compatible comm class. The base comm class also supports
# registering event handlers that can be notified when sockets are being
# created and have been created. This allows code to extend sockets on
# creation from the single point that they are created.
#
###
module Comm
###
#
# This mixin provides stubs for event notification handlers that can be
# registered with a Comm factory to be called when various events occur,
# such as socket instantiation.
#
###
module Events
#
# This callback is notified when a socket is being created and is passed
# the parameters that will be used to create it.
#
def on_before_socket_create(comm, param)
end
#
# This callback is notified when a new socket is created and the
# parameters that were used to create it. This provides the callback
# with a chance to extend or otherwise modify the socket before it's
# passed on to the actual requestor.
#
def on_socket_created(comm, sock, param)
end
end
#
# Creates a compatible socket based on the supplied uniform parameters.
#
def self.create(param)
raise NotImplementedError
end
#
# Indicates whether or not this comm can be chained with other chainable
# comms. This is particularly important for things like Proxy Comms that
# can be proxied through one another. The semantics of this are currently
# undefined and will probably need some more thought.
#
def chainable?
false
end
#
# Registers an event handler that implements the Rex::Socket::Comm::Event
# interface in at least some fashion. Event handlers are notified when
# sockets are created through the Comm instance that they register against.
#
def register_event_handler(handler)
if (handlers == nil)
self.handlers = []
end
self.handlers << handler
end
#
# Deregisters a previously registered event handler.
#
def deregister_event_handler(handler)
if (handlers)
handlers.delete(handler)
end
end
#
# Enumerates each registered event handler so that they can be notified of
# an event.
#
def each_event_handler(&block)
if (handlers)
handlers.each(&block)
end
end
#
# Notifies handlers of the before socket create event.
#
def notify_before_socket_create(comm, param)
each_event_handler() { |handler|
handler.on_before_socket_create(comm, param)
}
end
#
# Notifies handlers of the socket created event.
#
def notify_socket_created(comm, sock, param)
each_event_handler() { |handler|
handler.on_socket_created(comm, sock, param)
}
end
protected
attr_accessor :handlers # :nodoc:
attr_accessor :handlers_rwlock # :nodoc:
end
end
end

View File

@ -1,529 +0,0 @@
# -*- coding: binary -*-
require 'singleton'
require 'rex/socket'
require 'rex/socket/tcp'
require 'rex/socket/ssl_tcp'
require 'rex/socket/ssl_tcp_server'
require 'rex/socket/udp'
require 'rex/socket/ip'
require 'timeout'
###
#
# Local communication class factory.
#
###
class Rex::Socket::Comm::Local
include Singleton
include Rex::Socket::Comm
#
# Creates an instance of a socket using the supplied parameters.
#
def self.create(param)
# Work around jRuby socket implementation issues
if(RUBY_PLATFORM == 'java')
return self.create_jruby(param)
end
case param.proto
when 'tcp'
return create_by_type(param, ::Socket::SOCK_STREAM, ::Socket::IPPROTO_TCP)
when 'udp'
return create_by_type(param, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
when 'ip'
return create_ip(param)
else
raise Rex::UnsupportedProtocol.new(param.proto), caller
end
end
#
# Creates an instance of a socket using the supplied parameters.
# Use various hacks to make this work with jRuby
#
def self.create_jruby(param)
sock = nil
# Notify handlers of the before socket create event.
self.instance.notify_before_socket_create(self, param)
case param.proto
when 'tcp'
if (param.server?)
sock = TCPServer.new(param.localport, param.localhost)
klass = Rex::Socket::TcpServer
if (param.ssl)
klass = Rex::Socket::SslTcpServer
end
sock.extend(klass)
else
sock = TCPSocket.new(param.peerhost, param.peerport)
klass = Rex::Socket::Tcp
if (param.ssl)
klass = Rex::Socket::SslTcp
end
sock.extend(klass)
end
when 'udp'
if (param.server?)
sock = UDPServer.new(param.localport, param.localhost)
klass = Rex::Socket::UdpServer
sock.extend(klass)
else
sock = UDPSocket.new(param.peerhost, param.peerport)
klass = Rex::Socket::Udp
sock.extend(klass)
end
else
raise Rex::UnsupportedProtocol.new(param.proto), caller
end
sock.initsock(param)
self.instance.notify_socket_created(self, sock, param)
return sock
end
#
# Creates a raw IP socket using the supplied Parameter instance.
# Special-cased because of how different it is from UDP/TCP
#
def self.create_ip(param)
self.instance.notify_before_socket_create(self, param)
sock = ::Socket.open(::Socket::PF_INET, ::Socket::SOCK_RAW, ::Socket::IPPROTO_RAW)
sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_HDRINCL, 1)
# Configure broadcast support
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
if (param.bare? == false)
sock.extend(::Rex::Socket::Ip)
sock.initsock(param)
end
self.instance.notify_socket_created(self, sock, param)
sock
end
#
# Creates a socket using the supplied Parameter instance.
#
def self.create_by_type(param, type, proto = 0)
# Whether to use IPv6 addressing
usev6 = false
# Detect IPv6 addresses and enable IPv6 accordingly
if ( Rex::Socket.support_ipv6?())
# Allow the caller to force IPv6
if (param.v6)
usev6 = true
end
# Force IPv6 mode for non-connected UDP sockets
if (type == ::Socket::SOCK_DGRAM and not param.peerhost)
# FreeBSD allows IPv6 socket creation, but throws an error on sendto()
# Windows 7 SP1 and newer also fail to sendto with IPv6 udp sockets
unless Rex::Compat.is_freebsd or Rex::Compat.is_windows
usev6 = true
end
end
local = Rex::Socket.resolv_nbo(param.localhost) if param.localhost
peer = Rex::Socket.resolv_nbo(param.peerhost) if param.peerhost
if (local and local.length == 16)
usev6 = true
end
if (peer and peer.length == 16)
usev6 = true
end
if (usev6)
if (local and local.length == 4)
if (local == "\x00\x00\x00\x00")
param.localhost = '::'
elsif (local == "\x7f\x00\x00\x01")
param.localhost = '::1'
else
param.localhost = '::ffff:' + Rex::Socket.getaddress(param.localhost, true)
end
end
if (peer and peer.length == 4)
if (peer == "\x00\x00\x00\x00")
param.peerhost = '::'
elsif (peer == "\x7f\x00\x00\x01")
param.peerhost = '::1'
else
param.peerhost = '::ffff:' + Rex::Socket.getaddress(param.peerhost, true)
end
end
param.v6 = true
end
else
# No IPv6 support
param.v6 = false
end
# Notify handlers of the before socket create event.
self.instance.notify_before_socket_create(self, param)
# Create the socket
sock = nil
if (param.v6)
sock = ::Socket.new(::Socket::AF_INET6, type, proto)
else
sock = ::Socket.new(::Socket::AF_INET, type, proto)
end
# Bind to a given local address and/or port if they are supplied
if param.localport or param.localhost
begin
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
sock.bind(Rex::Socket.to_sockaddr(param.localhost, param.localport))
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
sock.close
raise Rex::BindFailed.new(param.localhost, param.localport), caller
end
end
# Configure broadcast support for all datagram sockets
if (type == ::Socket::SOCK_DGRAM)
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
end
# If a server TCP instance is being created...
if (param.server?)
sock.listen(256)
if (param.bare? == false)
klass = Rex::Socket::TcpServer
if (param.ssl)
klass = Rex::Socket::SslTcpServer
end
sock.extend(klass)
sock.initsock(param)
end
# Otherwise, if we're creating a client...
else
chain = []
# If we were supplied with host information
if (param.peerhost)
# A flag that indicates whether we need to try multiple scopes
retry_scopes = false
# Always retry with link-local IPv6 addresses
if Rex::Socket.is_ipv6?( param.peerhost ) and param.peerhost =~ /^fe80::/
retry_scopes = true
end
# Prepare a list of scope IDs to try when connecting to
# link-level addresses. Read from /proc if it is available,
# otherwise increment through the first 255 IDs.
@@ip6_lla_scopes ||= []
if @@ip6_lla_scopes.length == 0 and retry_scopes
# Linux specific interface lookup code
if ::File.exist?( "/proc/self/net/igmp6" )
::File.open("/proc/self/net/igmp6") do |fd|
fd.each_line do |line|
line = line.strip
tscope, tint, junk = line.split(/\s+/, 3)
next if not tint
# Specifying lo in any connect call results in the socket
# being unusable, even if the correct interface is set.
next if tint == "lo"
@@ip6_lla_scopes << tscope
end
end
else
# Other Unix-like platforms should support a raw scope ID
[*(1 .. 255)].map{ |x| @@ip6_lla_scopes << x.to_s }
end
end
ip6_scope_idx = 0
ip = param.peerhost
port = param.peerport
if param.proxies
chain = param.proxies.dup
chain.push(['host',param.peerhost,param.peerport])
ip = chain[0][1]
port = chain[0][2].to_i
end
begin
begin
Timeout.timeout(param.timeout) do
sock.connect(Rex::Socket.to_sockaddr(ip, port))
end
rescue ::Timeout::Error
raise ::Errno::ETIMEDOUT
end
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::ENOPROTOOPT
# Rescue errors caused by a bad Scope ID for a link-local address
if retry_scopes and @@ip6_lla_scopes[ ip6_scope_idx ]
ip = param.peerhost + "%" + @@ip6_lla_scopes[ ip6_scope_idx ]
ip6_scope_idx += 1
retry
end
sock.close
raise Rex::HostUnreachable.new(ip, port), caller
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
sock.close
raise Rex::InvalidDestination.new(ip, port), caller
rescue Errno::ETIMEDOUT
sock.close
raise Rex::ConnectionTimeout.new(ip, port), caller
rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED
sock.close
# Report the actual thing we were trying to connect to here, not
# param.peerhost, since that's the eventual target at the end of the
# proxy chain
raise Rex::ConnectionRefused.new(ip, port.to_i), caller
end
end
if (param.bare? == false)
case param.proto
when 'tcp'
klass = Rex::Socket::Tcp
sock.extend(klass)
sock.initsock(param)
when 'udp'
sock.extend(Rex::Socket::Udp)
sock.initsock(param)
end
end
if chain.size > 1
chain.each_with_index {
|proxy, i|
next_hop = chain[i + 1]
if next_hop
proxy(sock, proxy[0], next_hop[1], next_hop[2])
end
}
end
# Now extend the socket with SSL and perform the handshake
if(param.bare? == false and param.ssl)
klass = Rex::Socket::SslTcp
sock.extend(klass)
sock.initsock(param)
end
end
# Notify handlers that a socket has been created.
self.instance.notify_socket_created(self, sock, param)
sock
end
def self.proxy(sock, type, host, port)
case type.downcase
when 'sapni'
packet_type = 'NI_ROUTE'
route_info_version = 2
ni_version = 39
num_of_entries = 2
talk_mode = 1 # ref: http://help.sap.com/saphelp_dimp50/helpdata/En/f8/bb960899d743378ccb8372215bb767/content.htm
num_rest_nodes = 1
_af, shost, sport = sock.getpeername_as_array
first_route_item = [shost, 0, sport, 0, 0].pack("A*CA*cc")
route_data = [first_route_item.length, first_route_item].pack("NA*")
route_data << [host, 0, port.to_s, 0, 0].pack("A*CA*cc")
ni_packet = [
packet_type,
0,
route_info_version,
ni_version,
num_of_entries,
talk_mode,
0,
0,
num_rest_nodes
].pack("A8c8")
# Add the data block, according to sap documentation:
# A 4-byte header precedes each data block. These 4 bytes give the
# length of the data block (length without leading 4 bytes)
# The data block (the route data)
ni_packet << [route_data.length - 4].pack('N') + route_data
# Now that we've built the whole packet, prepend its length before writing it to the wire
ni_packet = [ni_packet.length].pack('N') + ni_packet
size = sock.put(ni_packet)
if size != ni_packet.length
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
end
begin
ret_len = sock.get_once(4, 30).unpack('N')[0]
if ret_len and ret_len != 0
ret = sock.get_once(ret_len, 30)
end
rescue IOError
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
end
if ret and ret.length < 4
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
end
if ret =~ /NI_RTERR/
case ret
when /timed out/
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to remote host #{host} timed out")
when /refused/
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to remote port #{port} closed")
when /denied/
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} blocked by ACL")
else
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
end
elsif ret =~ /NI_PONG/
# success case
# would like to print this "[*] remote native connection to #{host}:#{port} established\n"
else
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
end
when 'http'
setup = "CONNECT #{host}:#{port} HTTP/1.0\r\n\r\n"
size = sock.put(setup)
if (size != setup.length)
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
end
begin
ret = sock.get_once(39,30)
rescue IOError
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
end
if ret.nil?
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
end
resp = Rex::Proto::Http::Response.new
resp.update_cmd_parts(ret.split(/\r?\n/)[0])
if resp.code != 200
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy returned a non-OK response"), caller
end
when 'socks4'
setup = [4,1,port.to_i].pack('CCn') + Socket.gethostbyname(host)[3] + Rex::Text.rand_text_alpha(rand(8)+1) + "\x00"
size = sock.put(setup)
if (size != setup.length)
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
end
begin
ret = sock.get_once(8, 30)
rescue IOError
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
end
if (ret.nil? or ret.length < 8)
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
end
if ret[1,1] != "\x5a"
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{ret[0,1].unpack("C")[0]}"), caller
end
when 'socks5'
auth_methods = [5,1,0].pack('CCC')
size = sock.put(auth_methods)
if (size != auth_methods.length)
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
end
ret = sock.get_once(2,30)
if (ret[1,1] == "\xff")
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy requires authentication"), caller
end
if (Rex::Socket.is_ipv4?(host))
addr = Rex::Socket.gethostbyname(host)[3]
setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
elsif (Rex::Socket.support_ipv6? and Rex::Socket.is_ipv6?(host))
# IPv6 stuff all untested
addr = Rex::Socket.gethostbyname(host)[3]
setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
else
# Then it must be a domain name.
# Unfortunately, it looks like the host has always been
# resolved by the time it gets here, so this code never runs.
setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
end
size = sock.put(setup)
if (size != setup.length)
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
end
begin
response = sock.get_once(10, 30)
rescue IOError
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
end
if (response.nil? or response.length < 10)
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
end
if response[1,1] != "\x00"
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{response[1,1].unpack("C")[0]}"), caller
end
else
raise RuntimeError, "The proxy type specified is not valid", caller
end
end
##
#
# Registration
#
##
def self.register_event_handler(handler) # :nodoc:
self.instance.register_event_handler(handler)
end
def self.deregister_event_handler(handler) # :nodoc:
self.instance.deregister_event_handler(handler)
end
def self.each_event_handler(handler) # :nodoc:
self.instance.each_event_handler(handler)
end
end

View File

@ -1,132 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
###
#
# This class provides methods for interacting with a IP socket.
#
###
module Rex::Socket::Ip
include Rex::Socket
##
#
# Factory
#
##
#
# Creates the client using the supplied hash.
#
def self.create(hash = {})
hash['Proto'] = 'ip'
self.create_param(Rex::Socket::Parameters.from_hash(hash))
end
#
# Wrapper around the base socket class' creation method that automatically
# sets the parameter's protocol to IP.
#
def self.create_param(param)
param.proto = 'ip'
Rex::Socket.create_param(param)
end
##
#
# IP connected state methods
#
##
#
# Write the supplied datagram to the connected IP socket.
#
def write(gram)
raise RuntimeError, "IP sockets must use sendto(), not write()"
end
alias put write
#
# Read a datagram from the IP socket.
#
def read(length = 65535)
raise RuntimeError, "IP sockets must use recvfrom(), not read()"
end
##
#
# IP non-connected state methods
#
##
#
# Sends a datagram to the supplied host:port with optional flags.
#
def sendto(gram, peerhost, flags = 0)
dest = ::Socket.pack_sockaddr_in(0, peerhost)
# Some BSDs require byteswap for len and offset
if(
Rex::Compat.is_freebsd or
Rex::Compat.is_netbsd or
Rex::Compat.is_bsdi or
Rex::Compat.is_macosx
)
gram=gram.dup
# Note that these are *intentionally* host order for BSD support
gram[2,2]=gram[2,2].unpack("n").pack("s")
gram[6,2]=gram[6,2].unpack("n").pack("s")
end
begin
send(gram, flags, dest)
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
return nil
end
end
#
# Receives a datagram and returns the data and host of the requestor
# as [ data, host ].
#
def recvfrom(length = 65535, timeout=def_read_timeout)
begin
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
(rv[0]) and (rv[0][0] == fd)
)
data, saddr = super(length)
af, host = Rex::Socket.from_sockaddr(saddr)
return [ data, host ]
else
return [ '', nil ]
end
rescue Exception
return [ '', nil ]
end
end
#
# Calls recvfrom and only returns the data
#
def get(timeout=nil)
data, saddr = recvfrom(65535, timeout)
return data
end
#
# The default number of seconds to wait for a read operation to timeout.
#
def def_read_timeout
10
end
def type?
return 'ip'
end
end

View File

@ -1,372 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
###
#
# This class represents the set of parameters that are used to create
# a socket, whether it be a server or client socket.
#
# @example
# nsock = Rex::Socket::Tcp.create(
# 'PeerHost' => opts['RHOST'] || rhost,
# 'PeerPort' => (opts['RPORT'] || rport).to_i,
# 'LocalHost' => opts['CHOST'] || chost || "0.0.0.0",
# 'LocalPort' => (opts['CPORT'] || cport || 0).to_i,
# 'SSL' => dossl,
# 'SSLVersion'=> opts['SSLVersion'] || ssl_version,
# 'Proxies' => proxies,
# 'Timeout' => (opts['ConnectTimeout'] || connect_timeout || 10).to_i,
# 'Context' =>
# {
# 'Msf' => framework,
# 'MsfExploit' => self,
# })
#
###
class Rex::Socket::Parameters
##
#
# Factory
#
##
#
# Creates an instance of the Parameters class using the supplied hash.
#
def self.from_hash(hash)
return self.new(hash)
end
##
#
# Constructor
#
##
#
# Initializes the attributes from the supplied hash. The following hash
# keys can be specified.
#
# @option hash [String] 'PeerHost' The remote host to connect to
# @option hash [String] 'PeerAddr' (alias for 'PeerHost')
# @option hash [Fixnum] 'PeerPort' The remote port to connect to
# @option hash [String] 'LocalHost' The local host to communicate from, if any
# @option hash [String] 'LocalPort' The local port to communicate from, if any
# @option hash [Bool] 'Bool' Create a bare socket
# @option hash [Bool] 'Server' Whether or not this should be a server
# @option hash [Bool] 'SSL' Whether or not SSL should be used
# @option hash [OpenSSL::SSL::SSLContext] 'SSLContext' Use a pregenerated SSL Context
# @option hash [String] 'SSLVersion' Specify Auto, SSL2, SSL3, or TLS1 (Auto is
# default)
# @option hash [String] 'SSLCert' A file containing an SSL certificate (for
# server sockets)
# @option hash [String] 'SSLCipher' see {#ssl_cipher}
# @option hash [Bool] 'SSLCompression' enable SSL-level compression where available
# @option hash [String] 'SSLVerifyMode' SSL certificate verification
# mechanism. One of 'NONE' (default), 'CLIENT_ONCE', 'FAIL_IF_NO_PEER_CERT ', 'PEER'
# @option hash [String] 'Proxies' List of proxies to use.
# @option hash [String] 'Proto' The underlying protocol to use.
# @option hash [String] 'IPv6' Force the use of IPv6.
# @option hash [String] 'Comm' The underlying {Comm} object to use to create
# the socket for this parameter set.
# @option hash [Hash] 'Context' A context hash that can allow users of
# this parameter class instance to determine who is responsible for
# requesting that a socket be created.
# @option hash [String] 'Retries' The number of times a connection should be
# retried.
# @option hash [Fixnum] 'Timeout' The number of seconds before a connection
# should time out
def initialize(hash)
if (hash['PeerHost'])
self.peerhost = hash['PeerHost']
elsif (hash['PeerAddr'])
self.peerhost = hash['PeerAddr']
else
self.peerhost = nil
end
if (hash['LocalHost'])
self.localhost = hash['LocalHost']
elsif (hash['LocalAddr'])
self.localhost = hash['LocalAddr']
else
self.localhost = '0.0.0.0'
end
if (hash['PeerPort'])
self.peerport = hash['PeerPort'].to_i
else
self.peerport = 0
end
if (hash['LocalPort'])
self.localport = hash['LocalPort'].to_i
else
self.localport = 0
end
if (hash['Bare'])
self.bare = hash['Bare']
else
self.bare = false
end
if (hash['SSL'] and hash['SSL'].to_s =~ /^(t|y|1)/i)
self.ssl = true
else
self.ssl = false
end
if hash['SSLContext']
self.sslctx = hash['SSLContext']
end
supported_ssl_versions = ['Auto', 'SSL2', 'SSL23', 'TLS1', 'SSL3', :Auto, :SSLv2, :SSLv3, :SSLv23, :TLSv1]
if (hash['SSLVersion'] and supported_ssl_versions.include? hash['SSLVersion'])
self.ssl_version = hash['SSLVersion']
end
supported_ssl_verifiers = %W{CLIENT_ONCE FAIL_IF_NO_PEER_CERT NONE PEER}
if (hash['SSLVerifyMode'] and supported_ssl_verifiers.include? hash['SSLVerifyMode'])
self.ssl_verify_mode = hash['SSLVerifyMode']
end
if hash['SSLCompression']
self.ssl_compression = hash['SSLCompression']
end
if (hash['SSLCipher'])
self.ssl_cipher = hash['SSLCipher']
end
if (hash['SSLCert'] and ::File.file?(hash['SSLCert']))
begin
self.ssl_cert = ::File.read(hash['SSLCert'])
rescue ::Exception => e
elog("Failed to read cert: #{e.class}: #{e}", LogSource)
end
end
if hash['Proxies']
self.proxies = hash['Proxies'].split('-').map{|a| a.strip}.map{|a| a.split(':').map{|b| b.strip}}
end
# The protocol this socket will be using
if (hash['Proto'])
self.proto = hash['Proto'].downcase
else
self.proto = 'tcp'
end
# Whether or not the socket should be a server
self.server = hash['Server'] || false
# The communication subsystem to use to create the socket
self.comm = hash['Comm']
# The context that was passed in, if any.
self.context = hash['Context'] || {}
# If no comm was supplied, try to use the comm that is best fit to
# handle the provided host based on the current routing table.
if( self.server )
if (self.comm == nil and self.localhost)
self.comm = Rex::Socket::SwitchBoard.best_comm(self.localhost)
end
else
if (self.comm == nil and self.peerhost)
self.comm = Rex::Socket::SwitchBoard.best_comm(self.peerhost)
end
end
# If we still haven't found a comm, we default to the local comm.
self.comm = Rex::Socket::Comm::Local if (self.comm == nil)
# If we are a UDP server, turn off the server flag as it was only set when
# creating the UDP socket in order to avail of the switch board above.
if( self.server and self.proto == 'udp' )
self.server = false
end
# The number of connection retries to make (client only)
if hash['Retries']
self.retries = hash['Retries'].to_i
else
self.retries = 0
end
# The number of seconds before a connect attempt times out (client only)
if hash['Timeout']
self.timeout = hash['Timeout'].to_i
else
self.timeout = 5
end
# Whether to force IPv6 addressing
self.v6 = hash['IPv6'] || false
end
##
#
# Conditionals
#
##
#
# Returns true if this represents parameters for a server.
#
def server?
return (server == true)
end
#
# Returns true if this represents parameters for a client.
#
def client?
return (server == false)
end
#
# Returns true if the protocol for the parameters is TCP.
#
def tcp?
return (proto == 'tcp')
end
#
# Returns true if the protocol for the parameters is UDP.
#
def udp?
return (proto == 'udp')
end
#
# Returns true if the protocol for the parameters is IP.
#
def ip?
return (proto == 'ip')
end
#
# Returns true if the socket is a bare socket that does not inherit from
# any extended Rex classes.
#
def bare?
return (bare == true)
end
#
# Returns true if SSL has been requested.
#
def ssl?
return ssl
end
#
# Returns true if IPv6 has been enabled
#
def v6?
return v6
end
##
#
# Attributes
#
##
# The remote host information, equivalent to the PeerHost parameter hash
# key.
# @return [String]
attr_accessor :peerhost
# The remote port. Equivalent to the PeerPort parameter hash key.
# @return [Fixnum]
attr_accessor :peerport
# The local host. Equivalent to the LocalHost parameter hash key.
# @return [String]
attr_accessor :localhost
# The local port. Equivalent to the LocalPort parameter hash key.
# @return [Fixnum]
attr_accessor :localport
# The protocol to to use, such as TCP. Equivalent to the Proto parameter
# hash key.
# @return [String]
attr_accessor :proto
# Whether or not this is a server. Equivalent to the Server parameter
# hash key.
# @return [Bool]
attr_accessor :server
# The {Comm} instance that should be used to create the underlying socket.
# @return [Comm]
attr_accessor :comm
# The context hash that was passed in to the structure. (default: {})
# @return [Hash]
attr_accessor :context
# The number of attempts that should be made.
# @return [Fixnum]
attr_accessor :retries
# The number of seconds before a connection attempt should time out.
# @return [Fixnum]
attr_accessor :timeout
# Whether or not this is a bare (non-extended) socket instance that should
# be created.
# @return [Bool]
attr_accessor :bare
# Whether or not SSL should be used to wrap the connection.
# @return [Bool]
attr_accessor :ssl
# Pre configured SSL Context to use
# @return [OpenSSL::SSL::SSLContext]
attr_accessor :sslctx
# What version of SSL to use (Auto, SSL2, SSL3, SSL23, TLS1)
# @return [String,Symbol]
attr_accessor :ssl_version
# What specific SSL Cipher(s) to use, may be a string containing the cipher
# name or an array of strings containing cipher names e.g.
# ["DHE-RSA-AES256-SHA", "DHE-DSS-AES256-SHA"]
# @return [String,Array]
attr_accessor :ssl_cipher
# The SSL certificate, in pem format, stored as a string. See
# {Rex::Socket::SslTcpServer#makessl}
# @return [String]
attr_accessor :ssl_cert
# Enables SSL/TLS-level compression
# @return [Bool]
attr_accessor :ssl_compression
#
# The SSL context verification mechanism
#
attr_accessor :ssl_verify_mode
#
# Whether we should use IPv6
# @return [Bool]
attr_accessor :v6
# List of proxies to use
# @return [String]
attr_accessor :proxies
alias peeraddr peerhost
alias localaddr localhost
end

View File

@ -1,470 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
module Rex
module Socket
###
#
# This class provides an interface to enumerating an IP range
#
# This class uses start,stop pairs to represent ranges of addresses. This
# is very efficient for large numbers of consecutive addresses, and not
# show-stoppingly inefficient when storing a bunch of non-consecutive
# addresses, which should be a somewhat unusual case.
#
# @example
# r = RangeWalker.new("10.1,3.1-7.1-255")
# r.include?("10.3.7.255") #=> true
# r.length #=> 3570
# r.each do |addr|
# # do something with the address
# end
###
class RangeWalker
# The total number of IPs within the range
#
# @return [Fixnum]
attr_reader :length
# for backwards compatibility
alias :num_ips :length
# A list of the {Range ranges} held in this RangeWalker
# @return [Array]
attr_reader :ranges
# Initializes a walker instance using the supplied range
#
# @param parseme [RangeWalker,String]
def initialize(parseme)
if parseme.is_a? RangeWalker
@ranges = parseme.ranges.dup
else
@ranges = parse(parseme)
end
reset
end
#
# Calls the instance method
#
# This is basically only useful for determining if a range can be parsed
#
# @return (see #parse)
def self.parse(parseme)
self.new.parse(parseme)
end
#
# Turn a human-readable range string into ranges we can step through one address at a time.
#
# Allow the following formats:
# "a.b.c.d e.f.g.h"
# "a.b.c.d, e.f.g.h"
# where each chunk is CIDR notation, (e.g. '10.1.1.0/24') or a range in nmap format (see {#expand_nmap})
#
# OR this format
# "a.b.c.d-e.f.g.h"
# where a.b.c.d and e.f.g.h are single IPs and the second must be
# bigger than the first.
#
# @param parseme [String]
# @return [self]
# @return [false] if +parseme+ cannot be parsed
def parse(parseme)
return nil if not parseme
ranges = []
parseme.split(', ').map{ |a| a.split(' ') }.flatten.each do |arg|
opts = {}
# Handle IPv6 first (support ranges, but not CIDR)
if arg.include?(":")
addrs = arg.split('-', 2)
# Handle a single address
if addrs.length == 1
addr, scope_id = addrs[0].split('%')
opts[:scope_id] = scope_id if scope_id
opts[:ipv6] = true
return false unless Rex::Socket.is_ipv6?(addr)
addr = Rex::Socket.addr_atoi(addr)
ranges.push(Range.new(addr, addr, opts))
next
end
addr1, scope_id = addrs[0].split('%')
opts[:scope_id] = scope_id if scope_id
addr2, scope_id = addrs[0].split('%')
( opts[:scope_id] ||= scope_id ) if scope_id
# Both have to be IPv6 for this to work
return false unless (Rex::Socket.is_ipv6?(addr1) && Rex::Socket.is_ipv6?(addr2))
# Handle IPv6 ranges in the form of 2001::1-2001::10
addr1 = Rex::Socket.addr_atoi(addr1)
addr2 = Rex::Socket.addr_atoi(addr2)
ranges.push(Range.new(addr1, addr2, opts))
next
# Handle IPv4 CIDR
elsif arg.include?("/")
# Then it's CIDR notation and needs special case
return false if arg =~ /[,-]/ # Improper CIDR notation (can't mix with 1,3 or 1-3 style IP ranges)
return false if arg.scan("/").size > 1 # ..but there are too many slashes
ip_part,mask_part = arg.split("/")
return false if ip_part.nil? or ip_part.empty? or mask_part.nil? or mask_part.empty?
return false if mask_part !~ /^[0-9]{1,2}$/ # Illegal mask -- numerals only
return false if mask_part.to_i > 32 # This too -- between 0 and 32.
if ip_part =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
return false unless ip_part =~ Rex::Socket::MATCH_IPV4
end
begin
Rex::Socket.getaddress(ip_part) # This allows for "www.metasploit.com/24" which is fun.
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
return false # Can't resolve the ip_part, so bail.
end
expanded = expand_cidr(arg)
if expanded
ranges.push(expanded)
else
return false
end
# Handle hostnames
elsif arg =~ /[^-0-9,.*]/
# Then it's a domain name and we should send it on to addr_atoi
# unmolested to force a DNS lookup.
begin
ranges += Rex::Socket.addr_atoi_list(arg).map { |a| Range.new(a, a, opts) }
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
return false
end
# Handle IPv4 ranges
elsif arg =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})-([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/
# Then it's in the format of 1.2.3.4-5.6.7.8
# Note, this will /not/ deal with DNS names, or the fancy/obscure 10...1-10...2
begin
start, stop = Rex::Socket.addr_atoi($1), Rex::Socket.addr_atoi($2)
return false if start > stop # The end is greater than the beginning.
ranges.push(Range.new(start, stop, opts))
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
return false
end
else
# Returns an array of ranges
expanded = expand_nmap(arg)
if expanded
expanded.each { |r| ranges.push(r) }
end
end
end
# Remove any duplicate ranges
ranges = ranges.uniq
return ranges
end
#
# Resets the subnet walker back to its original state.
#
# @return [self]
def reset
return false if not valid?
@curr_range_index = 0
@curr_addr = @ranges.first.start
@length = 0
@ranges.each { |r| @length += r.length }
self
end
# Returns the next IP address.
#
# @return [String] The next address in the range
def next_ip
return false if not valid?
if (@curr_addr > @ranges[@curr_range_index].stop)
# Then we are at the end of this range. Grab the next one.
# Bail if there are no more ranges
return nil if (@ranges[@curr_range_index+1].nil?)
@curr_range_index += 1
@curr_addr = @ranges[@curr_range_index].start
end
addr = Rex::Socket.addr_itoa(@curr_addr, @ranges[@curr_range_index].ipv6?)
if @ranges[@curr_range_index].options[:scope_id]
addr = addr + '%' + @ranges[@curr_range_index].options[:scope_id]
end
@curr_addr += 1
return addr
end
alias :next :next_ip
# Whether this RangeWalker's ranges are valid
def valid?
(@ranges && !@ranges.empty?)
end
# Returns true if the argument is an ip address that falls within any of
# the stored ranges.
#
# @return [true] if this RangeWalker contains +addr+
# @return [false] if not
def include?(addr)
return false if not @ranges
if (addr.is_a? String)
addr = Rex::Socket.addr_atoi(addr)
end
@ranges.map { |r|
if addr.between?(r.start, r.stop)
return true
end
}
return false
end
#
# Returns true if this RangeWalker includes *all* of the addresses in the
# given RangeWalker
#
# @param other [RangeWalker]
def include_range?(other)
return false if (!@ranges || @ranges.empty?)
return false if !other.ranges || other.ranges.empty?
# Check that all the ranges in +other+ fall within at least one of
# our ranges.
other.ranges.all? do |other_range|
ranges.any? do |range|
other_range.start.between?(range.start, range.stop) && other_range.stop.between?(range.start, range.stop)
end
end
end
#
# Calls the given block with each address. This is basically a wrapper for
# {#next_ip}
#
# @return [self]
def each(&block)
while (ip = next_ip)
block.call(ip)
end
reset
self
end
#
# Returns an Array with one element, a {Range} defined by the given CIDR
# block.
#
# @see Rex::Socket.cidr_crack
# @param arg [String] A CIDR range
# @return [Range]
# @return [false] if +arg+ is not valid CIDR notation
def expand_cidr(arg)
start,stop = Rex::Socket.cidr_crack(arg)
if !start or !stop
return false
end
range = Range.new
range.start = Rex::Socket.addr_atoi(start)
range.stop = Rex::Socket.addr_atoi(stop)
range.options = { :ipv6 => (arg.include?(":")) }
return range
end
#
# Expands an nmap-style host range x.x.x.x where x can be simply "*" which
# means 0-255 or any combination and repitition of:
# i,n
# n-m
# i,n-m
# n-m,i
# ensuring that n is never greater than m.
#
# non-unique elements will be removed
# e.g.:
# 10.1.1.1-3,2-2,2 => ["10.1.1.1", "10.1.1.2", "10.1.1.3"]
# 10.1.1.1-3,7 => ["10.1.1.1", "10.1.1.2", "10.1.1.3", "10.1.1.7"]
#
# Returns an array of Ranges
#
def expand_nmap(arg)
# Can't really do anything with IPv6
return false if arg.include?(":")
# nmap calls these errors, but it's hard to catch them with our
# splitting below, so short-cut them here
return false if arg.include?(",-") or arg.include?("-,")
bytes = []
sections = arg.split('.')
if sections.length != 4
# Too many or not enough dots
return false
end
sections.each { |section|
if section.empty?
# pretty sure this is an unintentional artifact of the C
# functions that turn strings into ints, but it sort of makes
# sense, so why not
# "10...1" => "10.0.0.1"
section = "0"
end
if section == "*"
# I think this ought to be 1-254, but this is how nmap does it.
section = "0-255"
elsif section.include?("*")
return false
end
# Break down the sections into ranges like so
# "1-3,5-7" => ["1-3", "5-7"]
ranges = section.split(',', -1)
sets = []
ranges.each { |r|
bounds = []
if r.include?('-')
# Then it's an actual range, break it down into start,stop
# pairs:
# "1-3" => [ 1, 3 ]
# if the lower bound is empty, start at 0
# if the upper bound is empty, stop at 255
#
bounds = r.split('-', -1)
return false if (bounds.length > 2)
bounds[0] = 0 if bounds[0].nil? or bounds[0].empty?
bounds[1] = 255 if bounds[1].nil? or bounds[1].empty?
bounds.map!{|b| b.to_i}
return false if bounds[0] > bounds[1]
else
# Then it's a single value
bounds[0] = r.to_i
end
return false if bounds[0] > 255 or (bounds[1] and bounds[1] > 255)
return false if bounds[1] and bounds[0] > bounds[1]
if bounds[1]
bounds[0].upto(bounds[1]) do |i|
sets.push(i)
end
elsif bounds[0]
sets.push(bounds[0])
end
}
bytes.push(sets.sort.uniq)
}
#
# Combinitorically squish all of the quads together into a big list of
# ip addresses, stored as ints
#
# e.g.:
# [[1],[1],[1,2],[1,2]]
# =>
# [atoi("1.1.1.1"),atoi("1.1.1.2"),atoi("1.1.2.1"),atoi("1.1.2.2")]
addrs = []
for a in bytes[0]
for b in bytes[1]
for c in bytes[2]
for d in bytes[3]
ip = (a << 24) + (b << 16) + (c << 8) + d
addrs.push ip
end
end
end
end
addrs.sort!
addrs.uniq!
rng = Range.new
rng.options = { :ipv6 => false }
rng.start = addrs[0]
ranges = []
1.upto(addrs.length - 1) do |idx|
if addrs[idx - 1] + 1 == addrs[idx]
# Then this address is contained in the current range
next
else
# Then this address is the upper bound for the current range
rng.stop = addrs[idx - 1]
ranges.push(rng.dup)
rng.start = addrs[idx]
end
end
rng.stop = addrs[addrs.length - 1]
ranges.push(rng.dup)
return ranges
end
end
# A range of IP addresses
class Range
#@!attribute start
# The first address in this range, as a number
# @return [Fixnum]
attr_accessor :start
#@!attribute stop
# The last address in this range, as a number
# @return [Fixnum]
attr_accessor :stop
#@!attribute options
# @return [Hash]
attr_accessor :options
# @param start [Fixnum]
# @param stop [Fixnum]
# @param options [Hash] Recognized keys are:
# * +:ipv6+
# * +:scope_id+
def initialize(start=nil, stop=nil, options=nil)
@start = start
@stop = stop
@options = options
end
# Compare attributes with +other+
# @param other [Range]
# @return [Boolean]
def ==(other)
(other.start == start && other.stop == stop && other.ipv6? == ipv6? && other.options == options)
end
# The number of addresses in this Range
# @return [Fixnum]
def length
stop - start + 1
end
alias :count :length
# Whether this Range contains IPv6 or IPv4 addresses
# @return [Boolean]
def ipv6?
options[:ipv6]
end
end
end
end

View File

@ -1,46 +0,0 @@
module Rex
module Socket
# This class exists to abuse the Proxy capabilities in the Net::SSH library to allow the use of Rex::Sockets
# for the transport layer in Net::SSH. The SSHFactory object will respond to the #open method and create the
# {Rex::Socket::Tcp}
class SSHFactory
# @!attribute msfraemwork
# @return [Object] The framework instance object
attr_accessor :framework
# @!attribute msfmodule
# @return [Object] The metasploit module this socket belongs to
attr_accessor :msfmodule
# @!attribute proxies
# @return [String] Any proxies to use for the connection
attr_accessor :proxies
def initialize(framework, msfmodule, proxies)
@framework = framework
@msfmodule = msfmodule
@proxies = proxies
end
# Responds to the proxy setup routine Net::SSH will call when
# initialising the Transport Layer. This will instead create our
# {Rex::Socket::Tcp} and tie the socket back to the calling module
# @param host [String] The host to open the connection to
# @param port [Fixnum] the port to open the connection on
# @param options [Hash] the options hash
def open(host, port, options={})
socket = Rex::Socket::Tcp.create(
'PeerHost' => host,
'PeerPort' => port,
'Proxies' => proxies,
'Context' => {
'Msf' => framework,
'MsfExploit' => msfmodule
}
)
msfmodule.add_socket(socket) if msfmodule
socket
end
end
end
end

View File

@ -1,374 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
###
#
# This class provides methods for interacting with an SSL TCP client
# connection.
#
###
module Rex::Socket::SslTcp
begin
@@loaded_openssl = false
begin
require 'openssl'
@@loaded_openssl = true
require 'openssl/nonblock'
rescue ::Exception
end
include Rex::Socket::Tcp
##
#
# Factory
#
##
#
# Creates an SSL TCP instance.
#
def self.create(hash = {})
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
hash['SSL'] = true
self.create_param(Rex::Socket::Parameters.from_hash(hash))
end
#
# Set the SSL flag to true and call the base class's create_param routine.
#
def self.create_param(param)
param.ssl = true
Rex::Socket::Tcp.create_param(param)
end
##
#
# Class initialization
#
##
#
# Initializes the SSL socket.
#
def initsock(params = nil)
super
# Default to SSLv23 (automatically negotiate)
version = :SSLv23
# Let the caller specify a particular SSL/TLS version
if params
case params.ssl_version
when 'SSL2', :SSLv2
version = :SSLv2
# 'TLS' will be the new name for autonegotation with newer versions of OpenSSL
when 'SSL23', :SSLv23, 'TLS', 'Auto'
version = :SSLv23
when 'SSL3', :SSLv3
version = :SSLv3
when 'TLS1','TLS1.0', :TLSv1
version = :TLSv1
when 'TLS1.1', :TLSv1_1
version = :TLSv1_1
when 'TLS1.2', :TLSv1_2
version = :TLSv1_2
end
end
# Raise an error if no selected versions are supported
if ! OpenSSL::SSL::SSLContext::METHODS.include? version
raise ArgumentError, 'The system OpenSSL does not support the requested SSL/TLS version'
end
# Try intializing the socket with this SSL/TLS version
# This will throw an exception if it fails
initsock_with_ssl_version(params, version)
# Track the SSL version
self.ssl_negotiated_version = version
end
def initsock_with_ssl_version(params, version)
# Build the SSL connection
self.sslctx = OpenSSL::SSL::SSLContext.new(version)
# Configure the SSL context
# TODO: Allow the user to specify the verify mode callback
# Valid modes:
# VERIFY_CLIENT_ONCE
# VERIFY_FAIL_IF_NO_PEER_CERT
# VERIFY_NONE
# VERIFY_PEER
if params.ssl_verify_mode
self.sslctx.verify_mode = OpenSSL::SSL.const_get("VERIFY_#{params.ssl_verify_mode}".intern)
else
# Could also do this as graceful faildown in case a passed verify_mode is not supported
self.sslctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
self.sslctx.options = OpenSSL::SSL::OP_ALL
if params.ssl_cipher
self.sslctx.ciphers = params.ssl_cipher
end
# Set the verification callback
self.sslctx.verify_callback = Proc.new do |valid, store|
self.peer_verified = valid
true
end
# Tie the context to a socket
self.sslsock = OpenSSL::SSL::SSLSocket.new(self, self.sslctx)
# If peerhost looks like a hostname, set the undocumented 'hostname'
# attribute on sslsock, which enables the Server Name Indication (SNI)
# extension
self.sslsock.hostname = self.peerhost if !Rex::Socket.dotted_ip?(self.peerhost)
# Force a negotiation timeout
begin
Timeout.timeout(params.timeout) do
if not allow_nonblock?
self.sslsock.connect
else
begin
self.sslsock.connect_nonblock
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
IO::select(nil, nil, nil, 0.10)
retry
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
rescue ::Exception => e
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
IO::select( [ self.sslsock ], nil, nil, 0.10 )
retry
end
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
IO::select( nil, [ self.sslsock ], nil, 0.10 )
retry
end
raise e
end
end
end
rescue ::Timeout::Error
raise Rex::ConnectionTimeout.new(params.peerhost, params.peerport)
end
end
##
#
# Stream mixin implementations
#
##
#
# Writes data over the SSL socket.
#
def write(buf, opts = {})
return sslsock.write(buf) if not allow_nonblock?
total_sent = 0
total_length = buf.length
block_size = 16384
retry_time = 0.5
begin
while( total_sent < total_length )
s = Rex::ThreadSafe.select( nil, [ self.sslsock ], nil, 0.25 )
if( s == nil || s[0] == nil )
next
end
data = buf[total_sent, block_size]
sent = sslsock.write_nonblock( data )
if sent > 0
total_sent += sent
end
end
rescue ::IOError, ::Errno::EPIPE
return nil
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
# Sleep for a half a second, or until we can write again
Rex::ThreadSafe.select( nil, [ self.sslsock ], nil, retry_time )
# Decrement the block size to handle full sendQs better
block_size = 1024
# Try to write the data again
retry
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
rescue ::Exception => e
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
IO::select( [ self.sslsock ], nil, nil, retry_time )
retry
end
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
IO::select( nil, [ self.sslsock ], nil, retry_time )
retry
end
# Another form of SSL error, this is always fatal
if e.kind_of?(::OpenSSL::SSL::SSLError)
return nil
end
# Bubble the event up to the caller otherwise
raise e
end
total_sent
end
#
# Reads data from the SSL socket.
#
def read(length = nil, opts = {})
if not allow_nonblock?
length = 16384 unless length
begin
return sslsock.sysread(length)
rescue ::IOError, ::Errno::EPIPE, ::OpenSSL::SSL::SSLError
return nil
end
return
end
begin
while true
s = Rex::ThreadSafe.select( [ self.sslsock ], nil, nil, 0.10 )
if( s == nil || s[0] == nil )
next
end
return sslsock.read_nonblock( length )
end
rescue ::IOError, ::Errno::EPIPE
return nil
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
# Sleep for a tenth a second, or until we can read again
Rex::ThreadSafe.select( [ self.sslsock ], nil, nil, 0.10 )
# Decrement the block size to handle full sendQs better
block_size = 1024
# Try to write the data again
retry
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
rescue ::Exception => e
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
IO::select( [ self.sslsock ], nil, nil, 0.5 )
retry
end
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
IO::select( nil, [ self.sslsock ], nil, 0.5 )
retry
end
# Another form of SSL error, this is always fatal
if e.kind_of?(::OpenSSL::SSL::SSLError)
return nil
end
raise e
end
end
#
# Closes the SSL socket.
#
def close
sslsock.close rescue nil
super
end
#
# Ignore shutdown requests
#
def shutdown(how=0)
# Calling shutdown() on an SSL socket can lead to bad things
# Cause of http://metasploit.com/dev/trac/ticket/102
end
#
# Access to peer cert
#
def peer_cert
sslsock.peer_cert if sslsock
end
#
# Access to peer cert chain
#
def peer_cert_chain
sslsock.peer_cert_chain if sslsock
end
#
# Access to the current cipher
#
def cipher
sslsock.cipher if sslsock
end
#
# Prevent a sysread from the bare socket
#
def sysread(*args)
raise RuntimeError, "Invalid sysread() call on SSL socket"
end
#
# Prevent a sysread from the bare socket
#
def syswrite(*args)
raise RuntimeError, "Invalid syswrite() call on SSL socket"
end
#
# This flag determines whether to use the non-blocking openssl
# API calls when they are available. This is still buggy on
# Linux/Mac OS X, but is required on Windows
#
def allow_nonblock?
avail = self.sslsock.respond_to?(:accept_nonblock)
if avail and Rex::Compat.is_windows
return true
end
false
end
attr_reader :peer_verified # :nodoc:
attr_reader :ssl_negotiated_version # :nodoc:
attr_accessor :sslsock, :sslctx, :sslhash # :nodoc:
def type?
return 'tcp-ssl'
end
protected
attr_writer :peer_verified # :nodoc:
attr_writer :ssl_negotiated_version # :nodoc:
rescue LoadError
end
end

View File

@ -1,220 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'rex/socket/tcp_server'
require 'rex/io/stream_server'
require 'rex/parser/x509_certificate'
###
#
# This class provides methods for interacting with an SSL wrapped TCP server. It
# implements the StreamServer IO interface.
#
###
module Rex::Socket::SslTcpServer
@@loaded_openssl = false
begin
require 'openssl'
@@loaded_openssl = true
require 'openssl/nonblock'
rescue ::Exception
end
include Rex::Socket::TcpServer
##
#
# Factory
#
##
def self.create(hash = {})
hash['Proto'] = 'tcp'
hash['Server'] = true
hash['SSL'] = true
self.create_param(Rex::Socket::Parameters.from_hash(hash))
end
#
# Wrapper around the base class' creation method that automatically sets
# the parameter's protocol to TCP and sets the server flag to true.
#
def self.create_param(param)
param.proto = 'tcp'
param.server = true
param.ssl = true
Rex::Socket.create_param(param)
end
def initsock(params = nil)
raise RuntimeError, 'No OpenSSL support' unless @@loaded_openssl
if params && params.sslctx && params.sslctx.kind_of?(OpenSSL::SSL::SSLContext)
self.sslctx = params.sslctx
else
self.sslctx = makessl(params)
end
super
end
# (see TcpServer#accept)
def accept(opts = {})
sock = super()
return if not sock
begin
ssl = OpenSSL::SSL::SSLSocket.new(sock, self.sslctx)
if not allow_nonblock?(ssl)
ssl.accept
else
begin
ssl.accept_nonblock
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
IO::select(nil, nil, nil, 0.10)
retry
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
rescue ::Exception => e
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
IO::select( [ ssl ], nil, nil, 0.10 )
retry
end
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
IO::select( nil, [ ssl ], nil, 0.10 )
retry
end
raise e
end
end
sock.extend(Rex::Socket::SslTcp)
sock.sslsock = ssl
sock.sslctx = self.sslctx
return sock
rescue ::OpenSSL::SSL::SSLError
sock.close
nil
end
end
#
# Parse a certificate in unified PEM format that contains a private key and
# one or more certificates. The first certificate is the primary, while any
# additional certificates are treated as intermediary certificates. This emulates
# the behavior of web servers like nginx.
#
# @param [String] ssl_cert
# @return [String, String, Array]
def self.ssl_parse_pem(ssl_cert)
Rex::Parser::X509Certificate.parse_pem(ssl_cert)
end
#
# Shim for the ssl_parse_pem module method
#
def ssl_parse_pem(ssl_cert)
Rex::Socket::SslTcpServer.ssl_parse_pem(ssl_cert)
end
#
# Generate a realistic-looking but obstensibly fake SSL
# certificate. This matches a typical "snakeoil" cert.
#
# @return [String, String, Array]
def self.ssl_generate_certificate
yr = 24*3600*365
vf = Time.at(Time.now.to_i - rand(yr * 3) - yr)
vt = Time.at(vf.to_i + (10 * yr))
cn = Rex::Text.rand_text_alpha_lower(rand(8)+2)
key = OpenSSL::PKey::RSA.new(2048){ }
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = (rand(0xFFFFFFFF) << 32) + rand(0xFFFFFFFF)
cert.subject = OpenSSL::X509::Name.new([["CN", cn]])
cert.issuer = OpenSSL::X509::Name.new([["CN", cn]])
cert.not_before = vf
cert.not_after = vt
cert.public_key = key.public_key
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
cert.extensions = [
ef.create_extension("basicConstraints","CA:FALSE")
]
ef.issuer_certificate = cert
cert.sign(key, OpenSSL::Digest::SHA256.new)
[key, cert, nil]
end
#
# Shim for the ssl_generate_certificate module method
#
def ssl_generate_certificate
Rex::Socket::SslTcpServer.ssl_generate_certificate
end
#
# Create a new ssl context. If +ssl_cert+ is not given, generates a new
# key and a leaf certificate with random values.
#
# @param [Rex::Socket::Parameters] params
# @return [::OpenSSL::SSL::SSLContext]
def makessl(params)
if params.ssl_cert
key, cert, chain = ssl_parse_pem(params.ssl_cert)
else
key, cert, chain = ssl_generate_certificate
end
ctx = OpenSSL::SSL::SSLContext.new()
ctx.key = key
ctx.cert = cert
ctx.extra_chain_cert = chain
ctx.options = 0
if params.ssl_cipher
ctx.ciphers = params.ssl_cipher
end
# Older versions of OpenSSL do not export the OP_NO_COMPRESSION symbol
if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
# enable/disable the SSL/TLS-level compression
if params.ssl_compression
ctx.options &= ~OpenSSL::SSL::OP_NO_COMPRESSION
else
ctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION
end
end
ctx.session_id_context = Rex::Text.rand_text(16)
return ctx
end
#
# This flag determines whether to use the non-blocking openssl
# API calls when they are available. This is still buggy on
# Linux/Mac OS X, but is required on Windows
#
def allow_nonblock?(sock=self.sock)
avail = sock.respond_to?(:accept_nonblock)
if avail and Rex::Compat.is_windows
return true
end
false
end
attr_accessor :sslctx
end

View File

@ -1,76 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
module Rex
module Socket
###
#
# This class provides an interface to enumerating a subnet with a supplied
# netmask.
#
###
class SubnetWalker
#
# Initializes a subnet walker instance using the supplied subnet
# information.
#
def initialize(subnet, netmask)
self.subnet = Socket.resolv_to_dotted(subnet)
self.netmask = Socket.resolv_to_dotted(netmask)
reset
end
#
# Resets the subnet walker back to its original state.
#
def reset
self.curr_ip = self.subnet.split('.')
self.num_ips = (1 << (32 - Socket.net2bitmask(self.netmask).to_i))
self.curr_ip_idx = 0
end
#
# Returns the next IP address.
#
def next_ip
if (curr_ip_idx >= num_ips)
return nil
end
if (curr_ip_idx > 0)
self.curr_ip[3] = (curr_ip[3].to_i + 1) % 256
self.curr_ip[2] = (curr_ip[2].to_i + 1) % 256 if (curr_ip[3] == 0)
self.curr_ip[1] = (curr_ip[1].to_i + 1) % 256 if (curr_ip[2] == 0)
self.curr_ip[0] = (curr_ip[0].to_i + 1) % 256 if (curr_ip[1] == 0)
end
self.curr_ip_idx += 1
self.curr_ip.join('.')
end
#
# The subnet that is being enumerated.
#
attr_reader :subnet
#
# The netmask of the subnet.
#
attr_reader :netmask
#
# The total number of IPs within the subnet.
#
attr_reader :num_ips
protected
attr_writer :subnet, :netmask, :num_ips # :nodoc:
attr_accessor :curr_ip, :curr_ip_idx # :nodoc:
end
end
end

View File

@ -1,289 +0,0 @@
# -*- coding: binary -*-
require 'singleton'
require 'thread'
require 'rex/socket'
module Rex
module Socket
###
#
# This class provides a global routing table that associates subnets with Comm
# classes. Comm classes are used to instantiate objects that are tied to
# remote network entities. For example, the Local Comm class is used to
# building network connections directly from the local machine whereas, for
# instance, a Meterpreter Comm would build a local socket pair that is
# associated with a connection established by a remote entity. This can be
# seen as a uniform way of communicating with hosts through arbitrary
# channels.
#
###
class SwitchBoard
include Singleton
include Enumerable
def initialize
@_initialized = false
end
###
#
# This class represents a logical switch board route.
# TODO: Enable this to work with IPv6 addresses
#
###
class Route
def initialize(subnet, netmask, comm)
self.subnet = subnet
self.netmask = netmask
self.comm = comm
self.subnet_nbo = Socket.resolv_nbo_i(subnet)
self.netmask_nbo = Socket.resolv_nbo_i(netmask)
end
#
# Sort according to bitmask
#
def <=>(other)
self.bitmask <=> other.bitmask
end
#
# Convert the netmask to a bitmask and cache it.
#
def bitmask
@_bitmask = Socket.net2bitmask(self.netmask) if (@_bitmask == nil)
@_bitmask
end
attr_reader :subnet, :netmask, :comm
attr_reader :subnet_nbo, :netmask_nbo
protected
attr_writer :subnet, :netmask, :comm
attr_writer :subnet_nbo, :netmask_nbo
end
##
#
# Class method wrappers
#
##
#
# Adds a route to the switch board routing table using the supplied Comm
# instance.
#
def self.add_route(subnet, mask, comm)
ret = self.instance.add_route(subnet, mask, comm)
if ret && comm.respond_to?(:routes) && comm.routes.kind_of?(Array)
comm.routes << "#{subnet}/#{mask}"
end
ret
end
#
# Removes a route from the switch board routing table for the supplied
# subnet routing through the supplied Comm instance.
#
def self.remove_route(subnet, mask, comm)
ret = self.instance.remove_route(subnet, mask, comm)
if ret && comm.respond_to?(:routes) && comm.routes.kind_of?(Array)
comm.routes.delete "#{subnet}/#{mask}"
end
ret
end
#
# Flush all the routes from the switch board routing table.
#
def self.flush_routes
ret = self.instance.flush_routes
end
#
# Enumerate each route in the routing table.
#
def self.each(&block)
self.instance.each(&block)
end
#
# Returns the array of routes.
#
def self.routes
self.instance.routes
end
def self.route_exists?(subnet, mask)
self.instance.route_exists?(subnet, mask)
end
#
# Returns the Comm instance that should be used for the supplied address.
# If no comm can be found, the default Local Comm is returned.
#
def self.best_comm(addr)
self.instance.best_comm(addr)
end
#
# Removes all routes that go through the supplied Comm.
#
def self.remove_by_comm(comm)
self.instance.remove_by_comm(comm)
end
##
#
# Instance methods
#
##
#
# Adds a route for a given subnet and netmask destined through a given comm
# instance.
#
def add_route(subnet, mask, comm)
# If a bitmask was supplied, convert it.
netmask = (mask.to_s =~ /^\d+$/) ? Rex::Socket.bit2netmask(mask.to_i) : mask
rv = true
_init
mutex.synchronize {
# If the route already exists, return false to the caller.
if (route_exists?(subnet, netmask) == false)
self.routes << Route.new(subnet, netmask, comm)
else
rv = false
end
}
rv
end
#
# Removes a route for a given subnet and netmask destined through a given
# comm instance.
#
def remove_route(subnet, mask, comm)
# If a bitmask was supplied, convert it.
netmask = (mask.to_s =~ /^\d+$/) ? Rex::Socket.bit2netmask(mask.to_i) : mask
rv = false
_init
mutex.synchronize {
self.routes.delete_if { |route|
if (route.subnet == subnet and route.netmask == netmask and route.comm == comm)
rv = true
else
false
end
}
}
rv
end
#
# Flushes all established routes.
#
def flush_routes
_init
# Remove each of the individual routes so the comms don't think they're
# still routing after a flush.
self.routes.each { |r|
if r.comm.respond_to? :routes
r.comm.routes.delete("#{r.subnet}/#{r.netmask}")
end
}
# Re-initialize to an empty array
self.routes = Array.new
end
#
# Checks to see if a route already exists for the supplied subnet and
# netmask.
#
def route_exists?(subnet, netmask)
each { |route|
return true if (route.subnet == subnet and route.netmask == netmask)
}
false
end
#
# Enumerates each entry in the routing table.
#
def each(&block)
_init
routes.each(&block)
end
#
# Finds the best possible comm for the supplied target address.
#
def best_comm(addr)
addr_nbo = Socket.resolv_nbo_i(addr)
comm = nil
msb = 0
each { |route|
if ((route.subnet_nbo & route.netmask_nbo) ==
(addr_nbo & route.netmask_nbo))
if (route.bitmask >= msb)
comm = route.comm
msb = route.bitmask
end
end
}
comm
end
#
# Remove all routes that go through the supplied comm.
#
def remove_by_comm(comm)
_init
mutex.synchronize {
routes.delete_if { |route|
route.comm == comm
}
}
end
#
# The routes array.
#
attr_reader :routes
#
# The mutex protecting the routes array.
#
attr_reader :mutex
protected
attr_writer :routes, :mutex # :nodoc:
#
# Initializes the underlying stuff.
#
def _init
if (@_initialized != true)
@_initialized = true
self.routes = Array.new
self.mutex = Mutex.new
end
end
end
end
end

View File

@ -1,79 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'rex/io/stream'
###
#
# This class provides methods for interacting with a TCP client connection.
#
###
module Rex::Socket::Tcp
include Rex::Socket
include Rex::IO::Stream
##
#
# Factory
#
##
#
# Creates the client using the supplied hash.
#
# @see create_param
# @see Rex::Socket::Parameters.from_hash
def self.create(hash = {})
hash['Proto'] = 'tcp'
self.create_param(Rex::Socket::Parameters.from_hash(hash))
end
#
# Wrapper around the base socket class' creation method that automatically
# sets the parameter's protocol to TCP.
#
def self.create_param(param)
param.proto = 'tcp'
Rex::Socket.create_param(param)
end
##
#
# Stream mixin implementations
#
##
#
# Calls shutdown on the TCP connection.
#
def shutdown(how = ::Socket::SHUT_RDWR)
begin
return (super(how) == 0)
rescue ::Exception
end
end
#
# Returns peer information (host + port) in host:port format.
#
def peerinfo
if (pi = getpeername_as_array)
return pi[1] + ':' + pi[2].to_s
end
end
#
# Returns local information (host + port) in host:port format.
#
def localinfo
if (pi = getlocalname)
return pi[1] + ':' + pi[2].to_s
end
end
# returns socket type
def type?
return 'tcp'
end
end

View File

@ -1,70 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
require 'rex/socket/tcp'
require 'rex/io/stream_server'
###
#
# This class provides methods for interacting with a TCP server. It
# implements the Rex::IO::StreamServer interface.
#
###
module Rex::Socket::TcpServer
include Rex::Socket
include Rex::IO::StreamServer
##
#
# Factory
#
##
#
# Creates the server using the supplied hash.
#
def self.create(hash = {})
hash['Proto'] = 'tcp'
hash['Server'] = true
self.create_param(Rex::Socket::Parameters.from_hash(hash))
end
#
# Wrapper around the base class' creation method that automatically sets
# the parameter's protocol to TCP and sets the server flag to true.
#
def self.create_param(param)
param.proto = 'tcp'
param.server = true
Rex::Socket.create_param(param)
end
#
# Accepts a child connection.
#
def accept(opts = {})
t = super()
# jRuby compatibility
if t.respond_to?('[]')
t = t[0]
end
if (t)
t.extend(Rex::Socket::Tcp)
t.context = self.context
pn = t.getpeername_as_array
# We hit a "getpeername(2)" from Ruby
return nil unless pn
t.peerhost = pn[1]
t.peerport = pn[2]
end
t
end
end

View File

@ -1,165 +0,0 @@
# -*- coding: binary -*-
require 'rex/socket'
###
#
# This class provides methods for interacting with a UDP socket.
#
###
module Rex::Socket::Udp
include Rex::Socket
##
#
# Factory
#
##
#
# Creates the client using the supplied hash.
#
def self.create(hash = {})
hash['Proto'] = 'udp'
# If we have are to bind to a LocalHost we must be a Server to avail of pivoting.
# Rex::Socket::Parameters will subsequently turn off the sever flag after the correct
# comm has been chosen.
if( hash['LocalHost'] )
hash['Server'] = true
end
self.create_param(Rex::Socket::Parameters.from_hash(hash))
end
#
# Wrapper around the base socket class' creation method that automatically
# sets the parameter's protocol to UDP.
#
def self.create_param(param)
param.proto = 'udp'
Rex::Socket.create_param(param)
end
##
#
# UDP connected state methods
#
##
#
# Write the supplied datagram to the connected UDP socket.
#
def write(gram)
begin
return syswrite(gram)
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
return nil
end
end
alias put write
#
# Read a datagram from the UDP socket.
#
def read(length = 65535)
if length < 0
length = 65535
end
return sysread(length)
end
#
# Read a datagram from the UDP socket with a timeout
#
def timed_read(length = 65535, timeout=def_read_timeout)
begin
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
(rv[0]) and (rv[0][0] == fd)
)
return read(length)
else
return ''
end
rescue Exception
return ''
end
end
#alias send write
#alias recv read
##
#
# UDP non-connected state methods
#
##
#
# Sends a datagram to the supplied host:port with optional flags.
#
def sendto(gram, peerhost, peerport, flags = 0)
# Catch unconnected IPv6 sockets talking to IPv4 addresses
peer = Rex::Socket.resolv_nbo(peerhost)
if (peer.length == 4 and self.ipv == 6)
peerhost = Rex::Socket.getaddress(peerhost, true)
if peerhost[0,7].downcase != '::ffff:'
peerhost = '::ffff:' + peerhost
end
end
begin
send(gram, flags, Rex::Socket.to_sockaddr(peerhost, peerport))
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
return nil
end
end
#
# Receives a datagram and returns the data and host:port of the requestor
# as [ data, host, port ].
#
def recvfrom(length = 65535, timeout=def_read_timeout)
begin
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
(rv[0]) and (rv[0][0] == fd)
)
data, saddr = recvfrom_nonblock(length)
af, host, port = Rex::Socket.from_sockaddr(saddr)
return [ data, host, port ]
else
return [ '', nil, nil ]
end
rescue ::Timeout::Error
return [ '', nil, nil ]
rescue ::Interrupt
raise $!
rescue ::Exception
return [ '', nil, nil ]
end
end
#
# Calls recvfrom and only returns the data
#
def get(timeout=nil)
data, saddr, sport = recvfrom(65535, timeout)
return data
end
#
# The default number of seconds to wait for a read operation to timeout.
#
def def_read_timeout
10
end
def type?
return 'udp'
end
end

View File

@ -1,6 +0,0 @@
# -*- coding: binary -*-
require 'rex/sync/thread_safe'
require 'rex/sync/ref'
require 'rex/sync/read_write_lock'
require 'rex/sync/event'

View File

@ -1,85 +0,0 @@
# -*- coding: binary -*-
require 'thread'
module Rex
module Sync
###
#
# This class wraps the logical ConditionVariable class to make it an easier to
# work with interface that is similar to Windows' synchronization events.
#
###
class Event
Infinite = 10000
#
# Initializes a waitable event. The state parameter initializes the
# default state of the event. If auto_reset is true, any calls to set()
# will automatically reset the event back to an unset state.
#
def initialize(state = false, auto_reset = true, param = nil)
self.state = state
self.auto_reset = auto_reset
self.param = param
self.mutex = Mutex.new
self.cond = ConditionVariable.new
end
#
# Sets the event and wakes up anyone who was waiting.
#
def set(param = nil)
self.param = param
self.mutex.synchronize {
# If this event does not automatically reset its state,
# set the state to true
if (auto_reset == false)
self.state = true
end
self.cond.broadcast
}
end
#
# Resets the signaled state to false.
#
def reset
self.param = nil
self.state = false
end
#
# Alias notify with set.
#
alias notify set
#
# Waits for the event to become signaled. Timeout is measured in
# seconds. Raises TimeoutError if the condition does not become signaled.
#
def wait(t = Infinite)
self.mutex.synchronize {
break if (self.state == true)
Timeout.timeout(t) {
self.cond.wait(self.mutex)
}
}
return self.param
end
protected
attr_accessor :state, :auto_reset # :nodoc:
attr_accessor :param, :mutex, :cond # :nodoc:
end
end
end

View File

@ -1,177 +0,0 @@
# -*- coding: binary -*-
require 'thread'
module Rex
###
#
# This class implements a read/write lock synchronization
# primitive. It is meant to allow for more efficient access to
# resources that are more often read from than written to and many
# times can have concurrent reader threads. By allowing the reader
# threads to lock the resource concurrently rather than serially,
# a large performance boost can be seen. Acquiring a write lock
# results in exclusive access to the resource and thereby prevents
# any read operations during the time that a write lock is acquired.
# Only one write lock may be acquired at a time.
#
###
class ReadWriteLock
#
# Initializes a reader/writer lock instance.
#
def initialize
@read_sync_mutex = Mutex.new
@write_sync_mutex = Mutex.new
@exclusive_mutex = Mutex.new
@readers = 0
@writer = false
end
#
# Acquires the read lock for the calling thread.
#
def lock_read
read_sync_mutex.lock
begin
# If there are a non-zero number of readers and a
# writer is waiting to acquire the exclusive lock,
# free up the sync mutex temporarily and lock/unlock
# the exclusive lock. This is to give the writer
# thread a chance to acquire the lock and prevents
# it from being constantly starved.
if ((@readers > 0) and
(@writer))
read_sync_mutex.unlock
exclusive_mutex.lock
exclusive_mutex.unlock
read_sync_mutex.lock
end
# Increment the active reader count
@readers += 1
# If we now have just one reader, acquire the exclusive
# lock. Track the thread owner so that we release the
# lock from within the same thread context later on.
if (@readers == 1)
exclusive_mutex.lock
@owner = Thread.current
end
ensure
read_sync_mutex.unlock
end
end
#
# Releases the read lock for the calling thread.
#
def unlock_read
read_sync_mutex.lock
begin
unlocked = false
# Keep looping until we've lost this thread's reader
# lock
while (!unlocked)
# If there are no more readers left after this one
if (@readers - 1 == 0)
# If the calling thread is the owner of the exclusive
# reader lock, then let's release it
if (Thread.current == @owner)
@owner = nil
exclusive_mutex.unlock
end
# If there is more than one reader left and this thread is
# the owner of the exclusive lock, then keep looping so that
# we can eventually unlock the exclusive mutex in this thread's
# context
elsif (Thread.current == @owner)
read_sync_mutex.unlock
next
end
# Unlocked!
unlocked = true
# Decrement the active reader count
@readers -= 1
end
ensure
read_sync_mutex.unlock
end
end
#
# Acquire the exclusive write lock.
#
def lock_write
write_sync_mutex.lock
begin
@writer = true
exclusive_mutex.lock
@owner = Thread.current
ensure
write_sync_mutex.unlock
end
end
#
# Release the exclusive write lock.
#
def unlock_write
# If the caller is not the owner of the write lock, then someone is
# doing something broken, let's let them know.
if (Thread.current != @owner)
raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
end
# Otherwise, release the exclusive write lock
@writer = false
exclusive_mutex.unlock
end
#
# Synchronize a block for read access.
#
def synchronize_read
lock_read
begin
yield
ensure
unlock_read
end
end
#
# Synchronize a block for write access.
#
def synchronize_write
lock_write
begin
yield
ensure
unlock_write
end
end
protected
attr_accessor :read_sync_mutex # :nodoc:
attr_accessor :write_sync_mutex # :nodoc:
attr_accessor :exclusive_mutex # :nodoc:
end
end

View File

@ -1,58 +0,0 @@
# -*- coding: binary -*-
require 'thread'
module Rex
###
#
# This module provides a uniform reference counted interface for classes to
# use.
#
###
module Ref
#
# Initializes the reference count to one.
#
def refinit
@_references = 1
@_references_mutex = Mutex.new
self
end
#
# Increments the total number of references.
#
def ref
@_references_mutex.synchronize {
@_references += 1
}
self
end
#
# Decrements the total number of references. If the reference count
# reaches zero, true is returned. Otherwise, false is returned.
#
def deref
@_references_mutex.synchronize {
if ((@_references -= 1) == 0)
cleanup
true
else
false
end
}
end
#
# Called to clean up resources once the ref count drops to zero.
#
def cleanup
end
end
end

View File

@ -1,83 +0,0 @@
# -*- coding: binary -*-
require 'timeout'
module Rex
###
#
# This module provides a set of methods for performing various blocking
# operations in a manner that is compatible with ruby style threads.
#
###
module ThreadSafe
DefaultCycle = 0.2
#
# Wraps calls to select with a lower timeout period and does the
# calculations to walk down to zero timeout. This has a little room for
# improvement in that it should probably check how much time actually
# elapsed during the select call considering ruby threading wont be exactly
# accurate perhaps.
#
def self.select(rfd = nil, wfd = nil, efd = nil, t = nil)
left = t
# Immediately raise a StreamClosedError if the socket was closed. This
# prevents a bad fd from being passed downstream and solves an issue
# with Ruby on Windows.
rfd.each { |fd| raise StreamClosedError.new(fd) if (fd.closed?) } if rfd
begin
orig_size = rfd.length if (rfd)
# Poll the set supplied to us at least once.
begin
rv = ::IO.select(rfd, wfd, efd, DefaultCycle)
rescue ::IOError, ::Errno::EBADF, ::Errno::ENOTSOCK
# 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.
rfd.each { |fd| raise StreamClosedError.new(fd) if (fd.closed?) } if rfd
# 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 $!
# rescue ::Exception => e
# $stderr.puts "SELECT(#{t}) #{[rfd,wfd,efd].inspect} #{e.class} #{e} #{e.backtrace}"
end
return rv if (rv)
# Decrement the amount of time left by the polling cycle
left -= DefaultCycle if (left)
# Keep chugging until we run out of time, if time was supplied.
end while ((left == nil) or (left > 0))
# Nothin.
nil
end
#
# Simulates a sleep operation by selecting on nil until a timeout period
# expires.
#
def self.sleep(seconds=nil)
self.select(nil, nil, nil, seconds)
seconds
end
end
end

View File

@ -13,6 +13,7 @@ require 'rex/ui/text/input'
require 'rex/ui/text/shell'
require 'rex/ui/text/dispatcher_shell'
require 'rex/ui/text/irb_shell'
require 'rex/ui/text/bidirectional_pipe'
require 'rex/text/color'
require 'rex/text/table'

View File

@ -1,6 +1,7 @@
# -*- coding: binary -*-
module Rex
module IO
module Ui
module Text
require 'rex/ui/text/output'
require 'rex/ui/text/output/buffer'
@ -155,3 +156,4 @@ end
end
end
end

View File

@ -109,6 +109,8 @@ Gem::Specification.new do |spec|
#
# REX Libraries
#
# Core of the Ruby Exploitation Library
spec.add_runtime_dependency 'rex-core'
# Text manipulation library for things like generating random string
spec.add_runtime_dependency 'rex-text'
# Library for Generating Randomized strings valid as Identifiers such as variable names
@ -132,6 +134,8 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'rex-mime'
# Library for parsing and manipulating executable binaries
spec.add_runtime_dependency 'rex-bin_tools'
# Rex Socket Abstraction Layer
spec.add_runtime_dependency 'rex-socket'
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
# NoMethodError undefined method `dlopen' for Fiddle:Module

View File

@ -1,191 +0,0 @@
# -*- coding:binary -*-
require 'rex/socket/range_walker'
RSpec.describe Rex::Socket::RangeWalker do
let(:args) { "::1" }
subject(:walker) { described_class.new(args) }
it { is_expected.to respond_to(:length) }
it { is_expected.to respond_to(:valid?) }
it { is_expected.to respond_to(:each) }
describe '.new' do
context "with a hostname" do
let(:args) { "localhost" }
it { is_expected.to be_valid }
it { expect(subject.length).to be >= 1 }
end
context "with a hostname and CIDR" do
let(:args) { "localhost/24" }
it { is_expected.to be_valid }
it { expect(subject.length).to eq(256) }
end
context "with an invalid hostname" do
let(:args) { "@!*^&.invalid-hostname-really." }
it { is_expected.not_to be_valid }
end
context "with an invalid hostname and CIDR" do
let(:args) { "@!*^&.invalid-hostname-really./24" }
it { is_expected.not_to be_valid }
end
context "with an IPv6 address range containing a scope" do
let(:args) { "fe80::1%lo-fe80::100%lo" }
it { is_expected.to be_valid }
end
it "should handle single ipv6 addresses" do
walker = Rex::Socket::RangeWalker.new("::1")
expect(walker).to be_valid
expect(walker.length).to eq 1
end
it "should handle longform ranges" do
walker = Rex::Socket::RangeWalker.new("10.1.1.1-10.1.1.2")
expect(walker).to be_valid
expect(walker.length).to eq 2
expect(walker.next).to eq "10.1.1.1"
end
context "with mulitple ranges" do
let(:args) { "1.1.1.1-2 2.1-2.2.2 3.1-2.1-2.1 " }
it { is_expected.to be_valid }
it { expect(subject.length).to eq(8) }
it { is_expected.to include("1.1.1.1") }
end
it "should handle ranges" do
walker = Rex::Socket::RangeWalker.new("10.1.1.1-2")
expect(walker).to be_valid
expect(walker.length).to eq 2
expect(walker.next).to eq "10.1.1.1"
walker = Rex::Socket::RangeWalker.new("10.1-2.1.1-2")
expect(walker).to be_valid
expect(walker.length).to eq 4
walker = Rex::Socket::RangeWalker.new("10.1-2.3-4.5-6")
expect(walker).to be_valid
expect(walker.length).to eq 8
expect(walker).to include("10.1.3.5")
end
it 'should reject CIDR ranges with missing octets' do
walker = Rex::Socket::RangeWalker.new('192.168/24')
expect(walker).not_to be_valid
end
it 'should reject a CIDR range with too many octets' do
walker = Rex::Socket::RangeWalker.new('192.168.1.2.0/24')
expect(walker).not_to be_valid
end
it "should default the lower bound of a range to 0" do
walker = Rex::Socket::RangeWalker.new("10.1.3.-17")
expect(walker).to be_valid
expect(walker.length).to eq 18
walker = Rex::Socket::RangeWalker.new("10.1.3.-255")
expect(walker).to be_valid
expect(walker.length).to eq 256
end
it "should default the upper bound of a range to 255" do
walker = Rex::Socket::RangeWalker.new("10.1.3.254-")
expect(walker).to be_valid
expect(walker.length).to eq 2
end
it "should take * to mean 0-255" do
walker = Rex::Socket::RangeWalker.new("10.1.3.*")
expect(walker).to be_valid
expect(walker.length).to eq 256
expect(walker.next).to eq "10.1.3.0"
expect(walker).to include("10.1.3.255")
walker = Rex::Socket::RangeWalker.new("10.1.*.3")
expect(walker).to be_valid
expect(walker.length).to eq 256
expect(walker.next).to eq "10.1.0.3"
expect(walker).to include("10.1.255.3")
end
it "should handle lists" do
walker = Rex::Socket::RangeWalker.new("10.1.1.1")
expect(walker).to be_valid
expect(walker.length).to eq 1
walker = Rex::Socket::RangeWalker.new("10.1.1.1,3")
expect(walker).to be_valid
expect(walker.length).to eq 2
expect(walker).not_to include("10.1.1.2")
end
it "should produce the same ranges with * and 0-255" do
a = Rex::Socket::RangeWalker.new("10.1.3.*")
b = Rex::Socket::RangeWalker.new("10.1.3.0-255")
expect(a.ranges).to eq(b.ranges)
end
it "should handle ranges and lists together" do
walker = Rex::Socket::RangeWalker.new("10.1.1.1-2,3")
expect(walker).to be_valid
expect(walker.length).to eq 3
walker = Rex::Socket::RangeWalker.new("10.1-2.1.1,2")
expect(walker).to be_valid
expect(walker.length).to eq 4
walker = Rex::Socket::RangeWalker.new("10.1,2.3,4.5,6")
expect(walker.length).to eq 8
end
it "should handle cidr" do
31.downto 16 do |bits|
walker = Rex::Socket::RangeWalker.new("10.1.1.1/#{bits}")
expect(walker).to be_valid
expect(walker.length).to eq (2**(32-bits))
end
end
end
describe '#each' do
let(:args) { "10.1.1.1-2,2,3 10.2.2.2" }
it "should yield all ips" do
got = []
walker.each { |ip|
got.push ip
}
expect(got).to eq ["10.1.1.1", "10.1.1.2", "10.1.1.3", "10.2.2.2"]
end
end
describe '#include_range?' do
let(:args) { "10.1.1.*" }
it "returns true for a sub-range" do
other = described_class.new("10.1.1.1-255")
expect(walker).to be_include_range(other)
end
end
describe '#next' do
let(:args) { "10.1.1.1-5" }
it "should return all addresses" do
all = []
while ip = walker.next
all << ip
end
expect(all).to eq [ "10.1.1.1", "10.1.1.2", "10.1.1.3", "10.1.1.4", "10.1.1.5", ]
end
it "should not raise if called again after empty" do
expect {
(walker.length + 5).times { walker.next }
}.not_to raise_error
end
end
end

View File

@ -1,198 +0,0 @@
# -*- coding:binary -*-
require 'rex/socket/range_walker'
require 'spec_helper'
RSpec.describe Rex::Socket do
describe '.addr_itoa' do
context 'with explicit v6' do
it "should convert a number to a human-readable IPv6 address" do
expect(described_class.addr_itoa(1, true)).to eq "::1"
end
end
context 'with explicit v4' do
it "should convert a number to a human-readable IPv4 address" do
expect(described_class.addr_itoa(1, false)).to eq "0.0.0.1"
end
end
context 'without explicit version' do
it "should convert a number within the range of possible v4 addresses to a human-readable IPv4 address" do
expect(described_class.addr_itoa(0)).to eq "0.0.0.0"
expect(described_class.addr_itoa(1)).to eq "0.0.0.1"
expect(described_class.addr_itoa(0xffff_ffff)).to eq "255.255.255.255"
end
it "should convert a number larger than possible v4 addresses to a human-readable IPv6 address" do
expect(described_class.addr_itoa(0xfe80_0000_0000_0000_0000_0000_0000_0001)).to eq "fe80::1"
expect(described_class.addr_itoa(0x1_0000_0001)).to eq "::1:0:1"
end
end
end
describe '.addr_aton' do
subject(:nbo) do
described_class.addr_aton(try)
end
context 'with ipv6' do
let(:try) { "fe80::1" }
it { is_expected.to be_an(String) }
it { expect(subject.bytes.count).to eq(16) }
it "should be in the right order" do
expect(nbo).to eq "\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
end
end
context 'with ipv4' do
let(:try) { "127.0.0.1" }
it { is_expected.to be_an(String) }
it { expect(subject.bytes.count).to eq(4) }
it "should be in the right order" do
expect(nbo).to eq "\x7f\x00\x00\x01"
end
end
context 'with a hostname' do
let(:try) { "localhost" }
it "should resolve" do
expect(nbo).to be_a(String)
expect(nbo.encoding).to eq Encoding.find('binary')
expect([ 4, 16 ]).to include(nbo.length)
end
end
end
describe '.compress_address' do
subject(:compressed) do
described_class.compress_address(try)
end
context 'with lots of single 0s' do
let(:try) { "fe80:0:0:0:0:0:0:1" }
it { is_expected.to eq "fe80::1" }
end
end
describe '.getaddress' do
subject { described_class.getaddress('whatever') }
before(:example) do
expect(Socket).to receive(:gethostbyname).and_return(['name', ['aliases'], response_afamily, *response_addresses])
end
context 'when ::Socket.gethostbyname returns IPv4 responses' do
let(:response_afamily) { Socket::AF_INET }
let(:response_addresses) { ["\x01\x01\x01\x01", "\x02\x02\x02\x02"] }
it { is_expected.to be_a(String) }
it "should return the first ASCII address" do
expect(subject).to eq "1.1.1.1"
end
end
context 'when ::Socket.gethostbyname returns IPv6 responses' do
let(:response_afamily) { Socket::AF_INET6 }
let(:response_addresses) { ["\xfe\x80"+("\x00"*13)+"\x01", "\xfe\x80"+("\x00"*13)+"\x02"] }
it { is_expected.to be_a(String) }
it "should return the first ASCII address" do
expect(subject).to eq "fe80::1"
end
end
context "with rubinius' bug returning ASCII addresses" do
let(:response_afamily) { Socket::AF_INET }
let(:response_addresses) { ["1.1.1.1", "2.2.2.2"] }
it { is_expected.to be_a(String) }
it "should return the first ASCII address" do
expect(subject).to eq "1.1.1.1"
end
end
end
describe '.getaddresses' do
subject { described_class.getaddresses('whatever') }
before(:example) do
expect(Socket).to receive(:gethostbyname).and_return(['name', ['aliases'], response_afamily, *response_addresses])
end
context 'when ::Socket.gethostbyname returns IPv4 responses' do
let(:response_afamily) { Socket::AF_INET }
let(:response_addresses) { ["\x01\x01\x01\x01", "\x02\x02\x02\x02"] }
it { is_expected.to be_an(Array) }
it { expect(subject.size).to eq(2) }
it "should return the ASCII addresses" do
expect(subject).to include("1.1.1.1")
expect(subject).to include("2.2.2.2")
end
end
context 'when ::Socket.gethostbyname returns IPv6 responses' do
let(:response_afamily) { Socket::AF_INET6 }
let(:response_addresses) { ["\xfe\x80"+("\x00"*13)+"\x01", "\xfe\x80"+("\x00"*13)+"\x02"] }
it { is_expected.to be_an(Array) }
it { expect(subject.size).to eq(2) }
it "should return the ASCII addresses" do
expect(subject).to include("fe80::1")
expect(subject).to include("fe80::2")
end
end
context "with rubinius' bug returning ASCII addresses" do
let(:response_afamily) { Socket::AF_INET }
let(:response_addresses) { ["1.1.1.1", "2.2.2.2"] }
it { is_expected.to be_an(Array) }
it { expect(subject.size).to eq(2) }
it "should return the ASCII addresses" do
expect(subject).to include("1.1.1.1")
expect(subject).to include("2.2.2.2")
end
end
end
describe '.portspec_to_portlist' do
subject(:portlist) { described_class.portspec_to_portlist portspec_string}
let(:portspec_string) { '-1,0-10,!2-5,!7,65530-,65536' }
it 'does not include negative numbers' do
expect(portlist).to_not include '-1'
end
it 'does not include 0' do
expect(portlist).to_not include '0'
end
it 'does not include negated numbers' do
['2', '3', '4', '5', '7'].each do |port|
expect(portlist).to_not include port
end
end
it 'does not include any numbers above 65535' do
expect(portlist).to_not include '65536'
end
it 'expands open ended ranges' do
(65530..65535).each do |port|
expect(portlist).to include port
end
end
end
end