200 lines
4.4 KiB
Ruby
200 lines
4.4 KiB
Ruby
|
|
require 'msf/core'
|
||
|
|
require 'xdr'
|
||
|
|
|
||
|
|
module Msf
|
||
|
|
|
||
|
|
###
|
||
|
|
#
|
||
|
|
# This module exposes methods for accessing NDMP services using a class-based wrapper
|
||
|
|
# around normal sockets, allowing the use of more than one NDMP connection at once.
|
||
|
|
#
|
||
|
|
###
|
||
|
|
module Exploit::Remote::NDMPSocket
|
||
|
|
|
||
|
|
module NDMP
|
||
|
|
|
||
|
|
#
|
||
|
|
# This class represents a NDMP message, including its header and body.
|
||
|
|
#
|
||
|
|
class Message
|
||
|
|
class Header < XDR::Struct
|
||
|
|
attribute :sequence_num, XDR::Int
|
||
|
|
attribute :timestamp, XDR::Int
|
||
|
|
attribute :is_response, XDR::Bool
|
||
|
|
attribute :type, XDR::Int
|
||
|
|
attribute :reply_sequence_num, XDR::Int
|
||
|
|
attribute :error, XDR::Int
|
||
|
|
end
|
||
|
|
|
||
|
|
CONFIG_GET_HOST_INFO = 0x100
|
||
|
|
CONFIG_GET_BUTYPE_ATTR = 0x101
|
||
|
|
CONFIG_GET_SERVER_INFO = 0x108
|
||
|
|
NOTIFY_CONNECTED = 0x502
|
||
|
|
CONNECT_OPEN = 0x900
|
||
|
|
CONNECT_CLIENT_AUTH = 0x901
|
||
|
|
|
||
|
|
def self.new_request(type, body='')
|
||
|
|
header = Header.new(
|
||
|
|
:sequence_num => nil,
|
||
|
|
:timestamp => nil,
|
||
|
|
:is_response => false,
|
||
|
|
:type => type,
|
||
|
|
:reply_sequence_num => 0,
|
||
|
|
:error => 0
|
||
|
|
)
|
||
|
|
new(header, body)
|
||
|
|
end
|
||
|
|
|
||
|
|
attr_accessor :header, :body
|
||
|
|
|
||
|
|
def initialize(header, body)
|
||
|
|
@header = header
|
||
|
|
@body = body
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
#
|
||
|
|
# This class wraps a normal socket with NDMP functionality, such as NDMP message reading
|
||
|
|
# and writing.
|
||
|
|
#
|
||
|
|
class Socket
|
||
|
|
def initialize(sock)
|
||
|
|
@sock = sock
|
||
|
|
@next_sequence_num = 1
|
||
|
|
end
|
||
|
|
|
||
|
|
def raw_recv(*args)
|
||
|
|
@sock.recv(*args)
|
||
|
|
end
|
||
|
|
|
||
|
|
def raw_recvall(n, *args)
|
||
|
|
r = ''
|
||
|
|
while r.length < n
|
||
|
|
s = raw_recv(n - r.length, *args)
|
||
|
|
return nil if s.to_s.empty?
|
||
|
|
r << s
|
||
|
|
end
|
||
|
|
r
|
||
|
|
end
|
||
|
|
|
||
|
|
def raw_send(*args)
|
||
|
|
@sock.send(*args)
|
||
|
|
end
|
||
|
|
|
||
|
|
def raw_sendall(s, *args)
|
||
|
|
n = 0
|
||
|
|
while n < s.length
|
||
|
|
i = raw_send(s[n..s.length], *args)
|
||
|
|
return false if i <= 0
|
||
|
|
n += i
|
||
|
|
end
|
||
|
|
|
||
|
|
true
|
||
|
|
end
|
||
|
|
|
||
|
|
def close
|
||
|
|
@sock.close
|
||
|
|
end
|
||
|
|
|
||
|
|
#
|
||
|
|
# Read a single NDMP message. If require_ok_type is given, the message must be of the
|
||
|
|
# given type and have no error indicated.
|
||
|
|
#
|
||
|
|
def read_ndmp_msg(require_ok_type=nil)
|
||
|
|
frags = read_ndmp_frags
|
||
|
|
return nil if frags.nil?
|
||
|
|
header = Message::Header.from_xdr(frags.slice!(0...(4 * 6)))
|
||
|
|
|
||
|
|
return nil if require_ok_type && (require_ok_type != header.type || header.error != 0)
|
||
|
|
|
||
|
|
Message.new(header, frags)
|
||
|
|
end
|
||
|
|
|
||
|
|
#
|
||
|
|
# Prepare a NDMP message for sending and send it. Can send messages multiple times.
|
||
|
|
#
|
||
|
|
# If all_but_all_char is true, then the last character will be held back and will be
|
||
|
|
# returned so that it can be sent at a later point elsewhere. This is sometimes
|
||
|
|
# necessary for exploiting e.g. race conditions.
|
||
|
|
#
|
||
|
|
def prepare_and_write_ndmp_msg(msg, all_but_last_char=false, times=1, flags=0)
|
||
|
|
msg.header.sequence_num = @next_sequence_num
|
||
|
|
@next_sequence_num += 1
|
||
|
|
msg.header.timestamp = Time.now.to_i
|
||
|
|
|
||
|
|
frag = msg.header.to_xdr + msg.body
|
||
|
|
write_ndmp_frag(frag, all_but_last_char, times, flags)
|
||
|
|
end
|
||
|
|
|
||
|
|
#
|
||
|
|
# Send and recieve a pair of NDMP messages.
|
||
|
|
#
|
||
|
|
def do_request_response(msg, *args)
|
||
|
|
return nil unless prepare_and_write_ndmp_msg(msg, *args)
|
||
|
|
read_ndmp_msg(msg.header.type)
|
||
|
|
end
|
||
|
|
|
||
|
|
#
|
||
|
|
# Establish a SSL session on the socket. Raw socket reading/writing functions are
|
||
|
|
# replaced with their SSL equivalents.
|
||
|
|
#
|
||
|
|
def wrap_with_ssl(ssl_context)
|
||
|
|
@sock = OpenSSL::SSL::SSLSocket.new(@sock, ssl_context)
|
||
|
|
@sock.connect
|
||
|
|
|
||
|
|
def self.raw_recv(n, *_args)
|
||
|
|
@sock.sysread(n)
|
||
|
|
end
|
||
|
|
|
||
|
|
def self.raw_send(b, *_args)
|
||
|
|
@sock.syswrite(b)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
|
||
|
|
#
|
||
|
|
# Read and reassemble a group of NDMP fragments. Usually NDMP messages are sent in a
|
||
|
|
# single NDMP fragment, but this is not guaranteed by the standard.
|
||
|
|
#
|
||
|
|
def read_ndmp_frags
|
||
|
|
result = ''
|
||
|
|
|
||
|
|
loop do
|
||
|
|
buf = raw_recvall(4)
|
||
|
|
return nil if buf.nil?
|
||
|
|
n = buf.unpack('N')[0]
|
||
|
|
len = n & 0x7fffffff
|
||
|
|
last = (n & 0x80000000) != 0
|
||
|
|
|
||
|
|
buf = raw_recvall(len)
|
||
|
|
return nil if buf.nil?
|
||
|
|
result << buf
|
||
|
|
|
||
|
|
break if last
|
||
|
|
end
|
||
|
|
|
||
|
|
result
|
||
|
|
end
|
||
|
|
|
||
|
|
#
|
||
|
|
# Write a NDMP fragment (i.e. containing a NDMP packet).
|
||
|
|
#
|
||
|
|
# Can hold back on sending the last character; see prepare_and_write_ndmp_msg.
|
||
|
|
#
|
||
|
|
def write_ndmp_frag(buf, all_but_last_char, times, flags)
|
||
|
|
buf = ([buf.length | 0x80000000].pack('N') + buf) * times
|
||
|
|
|
||
|
|
return false unless raw_sendall(all_but_last_char ? buf[0...-1] : buf, flags)
|
||
|
|
|
||
|
|
all_but_last_char ? buf[-1] : true
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|