metasploit-framework/lib/rex/post/meterpreter/channel.rb

347 lines
7.3 KiB
Ruby

#!/usr/bin/ruby
require 'rex/post/meterpreter/inbound_packet_handler'
module Rex
module Post
module Meterpreter
#
# The various types of channels
#
CHANNEL_CLASS_STREAM = 1
CHANNEL_CLASS_DATAGRAM = 2
CHANNEL_CLASS_POOL = 3
#
# The various flags that can affect how the channel operates
#
# CHANNEL_FLAG_SYNCHRONOUS
# Specifies that I/O requests on the channel are blocking.
#
CHANNEL_FLAG_SYNCHRONOUS = (1 << 0)
#
# The core types of direct I/O requests
#
CHANNEL_DIO_READ = 'read'
CHANNEL_DIO_WRITE = 'write'
CHANNEL_DIO_CLOSE = 'close'
class Channel
# Class modifications to support global channel message
# dispatching without having to register a per-instance handler
class <<self
include Rex::Post::Meterpreter::InboundPacketHandler
# Class request handler for all channels that dispatches requests
# to the appropriate class instance's DIO handler
def request_handler(client, packet)
cid = packet.get_tlv_value(TLV_TYPE_CHANNEL_ID)
# No channel identifier, then drop out 'n shit
if (cid == nil)
return false
end
channel = client.find_channel(cid)
# Valid channel context?
if (channel == nil)
return false
end
dio = channel.dio_map(packet.method)
# Supported DIO request?
if (dio == nil)
return false
end
# Call the channel's dio handler and return success or fail
# based on what happens
return channel.dio_handler(dio, packet)
end
end
##
#
# Factory
#
##
# Creates a logical channel between the client and the server
# based on a given type.
def Channel.create(client, type = nil, klass = nil,
flags = CHANNEL_FLAG_SYNCHRONOUS, addends = nil)
request = Packet.create_request('core_channel_open')
# Set the type of channel that we're allocating
if (type != nil)
request.add_tlv(TLV_TYPE_CHANNEL_TYPE, type)
end
# If no factory class was provided, use the default native class
if (klass == nil)
klass = self
end
request.add_tlv(TLV_TYPE_CHANNEL_CLASS, klass.cls)
request.add_tlv(TLV_TYPE_FLAGS, flags)
request.add_tlvs(addends);
# Transmit the request and wait for the response
response = client.send_request(request)
cid = response.get_tlv(TLV_TYPE_CHANNEL_ID).value
# FIXME: race condition where data could be sent to the channel
# before it's added to the list.
# Create the channel instance
channel = klass.new(client, cid, type, flags)
# Insert the instance into the channel hash
if (channel != nil)
client.add_channel(channel)
end
return channel
end
##
#
# Constructor
#
##
# Initializes the instance's attributes, such as client context,
# class identifier, type, and flags
def initialize(client, cid, type, flags)
self.client = client
self.cid = cid
self.type = type
self.flags = flags
end
##
#
# Channel interaction
#
##
# Wrapper around the low-level channel read operation
def read(length = nil, addends = nil)
return _read(length, addends)
end
# Reads data from the remote half of the channel
def _read(length = nil, addends = nil)
if (self.cid == nil)
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request('core_channel_read')
if (length == nil)
length = 65536
end
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
request.add_tlv(TLV_TYPE_LENGTH, length)
request.add_tlvs(addends)
begin
response = self.client.send_request(request)
rescue
return nil
end
# If the channel is in synchronous mode, the response should contain
# data that was read from the remote side of the channel
if (flag?(CHANNEL_FLAG_SYNCHRONOUS))
data = response.get_tlv(TLV_TYPE_CHANNEL_DATA);
if (data != nil)
return data.value
end
else
raise NotImplementedError, "Asynchronous channel mode is not implemented", caller
end
return nil
end
# Wrapper around the low-level write
def write(buf, length = nil, addends = nil)
return _write(buf, length, addends)
end
# Writes data to the remote half of the channel
def _write(buf, length = nil, addends = nil)
if (self.cid == nil)
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request('core_channel_write')
# Truncation and celebration
if ((length != nil) &&
(buf.length >= length))
buf = buf[0..length]
else
length = buf.length
end
# Populate the request
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
request.add_tlv(TLV_TYPE_CHANNEL_DATA, buf)
request.add_tlv(TLV_TYPE_LENGTH, length)
request.add_tlvs(addends)
response = self.client.send_request(request)
written = response.get_tlv(TLV_TYPE_LENGTH)
return (written == nil) ? 0 : written.value
end
# Wrapper around the low-level close
def close(addends = nil)
return _close(addends)
end
# Close the channel for future writes
def close_write
return _close
end
# Close the channel for future reads
def close_read
return _close
end
# Closes the channel
def _close(addends = nil)
if (self.cid == nil)
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request('core_channel_close')
# Populate the request
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
request.add_tlvs(addends)
self.client.send_request(request)
# Disassociate this channel instance
self.client.remove_channel(self.cid)
self.cid = nil
return true
end
#
# Enables or disables interactive mode
#
def interactive(tf = true, addends = nil)
if (self.cid == nil)
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request('core_channel_interact')
# Populate the request
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
request.add_tlv(TLV_TYPE_BOOL, tf)
request.add_tlvs(addends)
self.client.send_request(request)
return true
end
##
#
# Direct I/O
#
##
# Handles dispatching I/O requests based on the request packet.
# The default implementation does nothing with direct I/O requests.
def dio_handler(dio, packet)
if (dio == CHANNEL_DIO_READ)
length = packet.get_tlv_value(TLV_TYPE_LENGTH)
return dio_read_handler(packet, length)
elsif (dio == CHANNEL_DIO_WRITE)
data = packet.get_tlv_value(TLV_TYPE_CHANNEL_DATA)
return dio_write_handler(packet, data)
elsif (dio == CHANNEL_DIO_CLOSE)
return dio_close_handler(packet)
end
return false;
end
# Stub read handler
def dio_read_handler(packet, length)
return false
end
# Stub write handler
def dio_write_handler(packet, data)
return false
end
# Stub close handler
def dio_close_handler(packet)
client.remove_channel(self)
return false
end
# Maps packet request methods to DIO request identifiers on a
# per-instance basis as other instances may add custom dio
# handlers.
def dio_map(method)
if (method == 'core_channel_read')
return CHANNEL_DIO_READ
elsif (method == 'core_channel_write')
return CHANNEL_DIO_WRITE
elsif (method == 'core_channel_close')
return CHANNEL_DIO_CLOSE
end
return nil
end
##
#
# Conditionals
#
##
# Checks to see if a flag is set on the instance's flags attribute
def flag?(flag)
return ((self.flags & flag) == flag)
end
# Returns whether or not the channel is operating synchronously
def synchronous?
return (self.flags & CHANNEL_FLAG_SYNCHRONOUS)
end
attr_reader :cid, :type, :cls, :flags
protected
attr_accessor :client
attr_writer :cid, :type, :cls, :flags
end
end; end; end