commit
24bb6b18ea
12
Gemfile.lock
12
Gemfile.lock
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue