Files
metasploit-gs/lib/msf/core/exploit/ndmp_socket.rb
T

200 lines
4.4 KiB
Ruby
Raw Normal View History

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