Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/dcerpc/client.rb
Views: 11704
# -*- coding: binary -*-1module Rex2module Proto3module DCERPC4class Client56require 'rex/text'78attr_accessor :handle, :socket, :options, :last_response, :context, :no_bind, :ispipe, :smb910# initialize a DCE/RPC Function Call11def initialize(handle, socket, useroptions = Hash.new)12self.handle = handle13self.socket = socket14self.options = {15'smb_user' => '',16'smb_pass' => '',17'smb_pipeio' => 'rw',18'smb_name' => nil,19'read_timeout' => 10,20'connect_timeout' => 521}2223self.options.merge!(useroptions)2425# If the caller passed us a smb_client object, use it and26# and skip the connect/login/ipc$ stages of the setup27if (self.options['smb_client'])28self.smb = self.options['smb_client']29end3031# we must have a valid handle, regardless of everything else32raise ArgumentError, 'handle is not a Rex::Proto::DCERPC::Handle' if !self.handle.is_a?(Rex::Proto::DCERPC::Handle)3334# we do this in case socket needs setup first, ie, socket = nil35if !self.options['no_socketsetup']36self.socket_check()37end3839raise ArgumentError, 'socket can not read' if !self.socket.respond_to?(:read)40raise ArgumentError, 'socket can not write' if !self.socket.respond_to?(:write)4142if !self.options['no_autobind']43self.bind()44end45end4647def socket_check()48if self.socket == nil49self.socket_setup()50end5152case self.handle.protocol53when 'ncacn_ip_tcp'54if self.socket.type? != 'tcp'55raise ::Rex::Proto::DCERPC::Exceptions::InvalidSocket, "ack, #{self.handle.protocol} requires socket type tcp, not #{self.socket.type?}!"56end57when 'ncacn_np'58if self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe59self.ispipe = 160elsif self.socket.type? == 'tcp'61self.smb_connect()62else63raise ::Rex::Proto::DCERPC::Exceptions::InvalidSocket, "ack, #{self.handle.protocol} requires socket type tcp, not #{self.socket.type?}!"64end65# No support ncacn_ip_udp (is it needed now that its ripped from Vista?)66else67raise ::Rex::Proto::DCERPC::Exceptions::InvalidSocket, "Unsupported protocol : #{self.handle.protocol}"68end69end7071# Create the appropriate socket based on protocol72def socket_setup()73ctx = { 'Msf' => self.options['Msf'], 'MsfExploit' => self.options['MsfExploit'] }74self.socket = case self.handle.protocol7576when 'ncacn_ip_tcp'77Rex::Socket.create_tcp(78'PeerHost' => self.handle.address,79'PeerPort' => self.handle.options[0],80'Context' => ctx,81'Timeout' => self.options['connect_timeout']82)8384when 'ncacn_np'85begin86socket = Rex::Socket.create_tcp(87'PeerHost' => self.handle.address,88'PeerPort' => 445,89'Context' => ctx,90'Timeout' => self.options['connect_timeout']91)92rescue ::Timeout::Error, Rex::ConnectionRefused93socket = Rex::Socket.create_tcp(94'PeerHost' => self.handle.address,95'PeerPort' => 139,96'Context' => ctx,97'Timeout' => self.options['connect_timeout']98)99end100socket101else nil102end103104# Add this socket to the exploit's list of open sockets105options['MsfExploit'].add_socket(self.socket) if (options['MsfExploit'])106end107108def smb_connect()109110if(not self.smb)111if self.socket.peerport == 139112smb = Rex::Proto::SMB::SimpleClient.new(self.socket)113else114smb = Rex::Proto::SMB::SimpleClient.new(self.socket, true)115end116117smb.login('*SMBSERVER', self.options['smb_user'], self.options['smb_pass'])118smb.connect("\\\\#{self.handle.address}\\IPC$")119self.smb = smb120self.smb.client.read_timeout = self.options['read_timeout']121end122123f = self.smb.create_pipe(self.handle.options[0])124f.mode = self.options['smb_pipeio']125self.socket = f126end127128def read()129130max_read = self.options['pipe_read_max_size'] || 1024*1024131min_read = self.options['pipe_read_min_size'] || max_read132133raw_response = ''134135# Are we reading from a remote pipe over SMB?136if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe)137begin138raw_response = self.socket.read(65535, 0)139rescue Rex::Proto::SMB::Exceptions::NoReply140# I don't care if I didn't get a reply...141rescue Rex::Proto::SMB::Exceptions::ErrorCode => exception142if exception.error_code != 0xC000014B143raise exception144end145end146# This must be a regular TCP or UDP socket147else148if (self.socket.type? == 'tcp')149if (false and max_read)150while (true)151data = self.socket.get_once((rand(max_read-min_read)+min_read), self.options['read_timeout'])152break if not data153break if not data.length154raw_response << data155end156else157# Just read the entire response in one go158raw_response = self.socket.get_once(-1, self.options['read_timeout'])159end160else161# No segmented read support for non-TCP sockets162raw_response = self.socket.read(0xFFFFFFFF / 2 - 1) # read max data163end164end165166raw_response167end168169# Write data to the underlying socket, limiting the sizes of the writes based on170# the pipe_write_min / pipe_write_max options.171def write(data)172173max_write = self.options['pipe_write_max_size'] || data.length174min_write = self.options['pipe_write_min_size'] || max_write175176if(min_write > max_write)177max_write = min_write178end179180idx = 0181182if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe)183while(idx < data.length)184bsize = (rand(max_write-min_write)+min_write).to_i185len = self.socket.write(data[idx, bsize])186idx += bsize187end188else189self.socket.write(data)190end191192data.length193end194195def bind()196bind = ''197context = ''198if self.options['fake_multi_bind']199200args = [ self.handle.uuid[0], self.handle.uuid[1] ]201202if (self.options['fake_multi_bind_prepend'])203args << self.options['fake_multi_bind_prepend']204end205206if (self.options['fake_multi_bind_append'])207args << self.options['fake_multi_bind_append']208end209210bind, context = Rex::Proto::DCERPC::Packet.make_bind_fake_multi(*args)211else212bind, context = Rex::Proto::DCERPC::Packet.make_bind(*self.handle.uuid)213end214215raise ::Rex::Proto::DCERPC::Exceptions::BindError, 'make_bind failed' if !bind216217self.write(bind)218raw_response = self.read()219220response = Rex::Proto::DCERPC::Response.new(raw_response)221self.last_response = response222if response.type == 12 or response.type == 15223if self.last_response.ack_result[context] == 2224raise ::Rex::Proto::DCERPC::Exceptions::BindError, "Could not bind to #{self.handle}"225end226self.context = context227else228raise ::Rex::Proto::DCERPC::Exceptions::BindError, "Could not bind to #{self.handle}"229end230end231232# Perform a DCE/RPC Function Call233def call(function, data, do_recv = true)234235frag_size = data.length236if options['frag_size']237frag_size = options['frag_size']238end239object_id = ''240if options['object_call']241object_id = self.handle.uuid[0]242end243if options['random_object_id']244object_id = Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))245end246247call_packets = Rex::Proto::DCERPC::Packet.make_request(function, data, frag_size, self.context, object_id)248call_packets.each { |packet|249self.write(packet)250}251252return true if not do_recv253254raw_response = ''255data = ''256last_frag = false257258until last_frag do259begin260raw_response = self.read()261rescue ::EOFError262raise Rex::Proto::DCERPC::Exceptions::NoResponse263end264265if (raw_response == nil or raw_response.length == 0)266raise Rex::Proto::DCERPC::Exceptions::NoResponse267end268269self.last_response = Rex::Proto::DCERPC::Response.new(raw_response)270271if self.last_response.type == 3272e = Rex::Proto::DCERPC::Exceptions::Fault.new273e.fault = self.last_response.status274raise e275end276277data << self.last_response.stub_data278last_frag = (self.last_response.flags & Rex::Proto::DCERPC::Response::FLAG_LAST_FRAG) == Rex::Proto::DCERPC::Response::FLAG_LAST_FRAG279end280281data282end283284# Process a DCERPC response packet from a socket285def self.read_response(socket, timeout=self.options['read_timeout'])286287data = socket.get_once(-1, timeout)288289# We need at least 10 bytes to find the FragLen290if (! data or data.length() < 10)291return292end293294# Pass the first 10 bytes to the constructor295resp = Rex::Proto::DCERPC::Response.new(data.slice!(0, 10))296297# Something went wrong in the parser...298if (! resp.frag_len)299return resp300end301302# Do we need to read more data?303if (resp.frag_len > (data.length + 10))304begin305data << socket.timed_read(resp.frag_len - data.length - 10, timeout)306rescue Timeout::Error307end308end309310# Still missing some data...311if (data.length() != resp.frag_len - 10)312# TODO: Bubble this up somehow313# $stderr.puts "Truncated DCERPC response :-("314return resp315end316317resp.parse(data)318return resp319end320321end322end323end324end325326327328