# -*- coding: binary -*- require 'timeout' require 'rex/sync/thread_safe' require 'rex/socket/udp' require 'rex/socket/parameters' require 'rex/post/meterpreter/extensions/stdapi/tlv' require 'rex/post/meterpreter/channel' module Rex module Post module Meterpreter module Extensions module Stdapi module Net module SocketSubsystem class UdpChannel < Rex::Post::Meterpreter::Channel # # We inclue Rex::Socket::Udp as this channel is effectivly a UDP socket. # include Rex::Socket::Udp # # We are a datagram channel. # class << self def cls return CHANNEL_CLASS_DATAGRAM end end # # Open a new UDP channel on the remote end. The local host/port are optional, if none are specified # the remote end will bind to INADDR_ANY with a random port number. The peer host/port are also # optional, if specified all default send(), write() call will sendto the specified peer. If no peer # host/port is specified you must use sendto() and specify the remote peer you wish to send to. This # effectivly lets us create bound/unbound and connected/unconnected UDP sockets with ease. # def UdpChannel.open(client, params) c = Channel.create(client, 'stdapi_net_udp_client', self, CHANNEL_FLAG_SYNCHRONOUS, [ { 'type' => TLV_TYPE_LOCAL_HOST, 'value' => params.localhost }, { 'type' => TLV_TYPE_LOCAL_PORT, 'value' => params.localport }, { 'type' => TLV_TYPE_PEER_HOST, 'value' => params.peerhost }, { 'type' => TLV_TYPE_PEER_PORT, 'value' => params.peerport } ] ) c.params = params c end # # Simply initialize this instance. # def initialize(client, cid, type, flags) super(client, cid, type, flags) # the instance variable that holds all incoming datagrams. @datagrams = [] end # # We overwrite Rex::Socket::Udp.timed_read in order to avoid the call to Kernel.select # which wont be of use as we are not a natively backed ::Socket or ::IO instance. # def timed_read( length=65535, timeout=def_read_timeout ) result = '' begin Timeout.timeout( timeout ) { while( true ) if( @datagrams.empty? ) Rex::ThreadSafe.sleep( 0.2 ) next end result = self.read( length ) break end } rescue Timeout::Error result = '' end return result end # # We overwrite Rex::Socket::Udp.recvfrom in order to correctly hand out the # datagrams which the remote end of this channel has received and are in the # queue. # def recvfrom( length=65535, timeout=def_read_timeout ) result = nil # force a timeout on the wait for an incoming datagram begin Timeout.timeout( timeout ) { while( true ) # wait untill we have at least one datagram in the queue if( @datagrams.empty? ) Rex::ThreadSafe.sleep( 0.2 ) next end # grab the oldest datagram we have received... result = @datagrams.shift # break as we have a result... break end } rescue Timeout::Error result = nil end # if no result return nothing if( result == nil ) return [ '', nil, nil ] end # get the data from this datagram data = result[0] # if its only a partial read of this datagram, slice it, loosing the remainder. result[0] = data[0,length-1] if data.length > length # return the result in the form [ data, host, port ] return result end # # Overwrite the low level sysread to read data off our datagram queue. Calls # to read() will end up calling this. # def sysread( length ) result = self.recvfrom( length ) return result[0] end # # Overwrite the low level syswrite to write data to the remote end of the channel. # Calls to write() will end up calling this. # def syswrite( buf ) return _write( buf ) end # # This function is called by Rex::Socket::Udp.sendto and writes data to a specified # remote peer host/port via the remote end of the channel. # def send( buf, flags, saddr ) af, peerhost, peerport = Rex::Socket.from_sockaddr( saddr ) addends = [ { 'type' => TLV_TYPE_PEER_HOST, 'value' => peerhost }, { 'type' => TLV_TYPE_PEER_PORT, 'value' => peerport } ] return _write( buf, buf.length, addends ) end # # The channels direct io write handler for any incoming data from the remote end # of the channel. We extract the data and peer host/port, and save this to a queue # of incoming datagrams which are passed out via calls to self.recvfrom() # def dio_write_handler( packet, data ) peerhost = packet.get_tlv_value( TLV_TYPE_PEER_HOST ) peerport = packet.get_tlv_value( TLV_TYPE_PEER_PORT ) if( peerhost and peerport ) @datagrams << [ data, peerhost, peerport ] return true end return false end # # Wrap the _write() call in order to catch some common, but harmless Windows exceptions # def _write(*args) begin super(*args) rescue ::Rex::Post::Meterpreter::RequestError => e case e.code when 10000 .. 10100 raise ::Rex::ConnectionError.new end end end end end; end; end; end; end; end; end