class Rex::Post::Meterpreter::Channel

The channel class represents a logical data pipe that exists between the client and the server. The purpose and behavior of the channel depends on which type it is. The three basic types of channels are streams, datagrams, and pools. Streams are basically equivalent to a TCP connection. Bidirectional, connection-oriented streams. Datagrams are basically equivalent to a UDP session. Bidirectional, connectionless. Pools are basically equivalent to a uni-directional connection, like a file handle. Pools denote channels that only have requests flowing in one direction.

Attributes

cid[R]

The unique channel identifier.

client[RW]

The associated meterpreter client instance

cls[R]

The class of channel (stream, datagram, pool).

flags[R]

Any channel-specific flag, like synchronous IO.

params[RW]

Any channel-specific parameters.

type[R]

The type of channel.

Public Class Methods

_close(client, cid, addends=nil) click to toggle source

Closes the channel.

# File lib/rex/post/meterpreter/channel.rb, line 272
def self._close(client, cid, addends=nil)
  if (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, cid)
  request.add_tlvs(addends)

  client.send_request(request, nil)

  # Disassociate this channel instance
  client.remove_channel(cid)

  return true
end
create(client, type = nil, klass = nil, flags = CHANNEL_FLAG_SYNCHRONOUS, addends = nil) click to toggle source

Creates a logical channel between the client and the server based on a given type.

# File lib/rex/post/meterpreter/channel.rb, line 95
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

  # Create the channel instance
  channel  = klass.new(client, cid, type, flags)

  return channel
end
finalize(client,cid) click to toggle source
# File lib/rex/post/meterpreter/channel.rb, line 148
def self.finalize(client,cid)
  proc { self._close(client,cid) }
end
new(client, cid, type, flags) click to toggle source

Initializes the instance's attributes, such as client context, class identifier, type, and flags.

# File lib/rex/post/meterpreter/channel.rb, line 133
def initialize(client, cid, type, flags)
  self.client = client
  self.cid    = cid
  self.type   = type
  self.flags  = flags

  # Add this instance to the list
  if (cid and client)
    client.add_channel(self)
  end

  # Ensure the remote object is closed when all references are removed
  ObjectSpace.define_finalizer(self, self.class.finalize(client, cid))
end
request_handler(client, packet) click to toggle source

Class request handler for all channels that dispatches requests to the appropriate class instance's DIO handler

# File lib/rex/post/meterpreter/channel.rb, line 55
def request_handler(client, packet)
  cid = packet.get_tlv_value(TLV_TYPE_CHANNEL_ID)

  # No channel identifier, then drop it
  if (cid == nil)
    return false
  end

  channel = client.find_channel(cid)

  # No valid channel context? The channel may not be registered yet
  if (channel == nil)
    return false
  end


  dio = channel.dio_map(packet.method)

  # Supported DIO request? Dump it.
  if (dio == nil)
    return true
  end


  # Call the channel's dio handler and return success or fail
  # based on what happens
  channel.dio_handler(dio, packet)
end

Public Instance Methods

_close(addends = nil) click to toggle source
# File lib/rex/post/meterpreter/channel.rb, line 291
def _close(addends = nil)
  unless self.cid.nil?
    ObjectSpace.undefine_finalizer(self)
    self.class._close(self.client, self.cid, addends)
    self.cid = nil
  end
end
_read(length = nil, addends = nil) click to toggle source

Reads data from the remote half of the channel.

# File lib/rex/post/meterpreter/channel.rb, line 168
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)
    # Default block size to a higher amount for passive dispatcher
    length = self.client.passive_service ? (1024*1024) : 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
_write(buf, length = nil, addends = nil) click to toggle source

Writes data to the remote half of the channel.

# File lib/rex/post/meterpreter/channel.rb, line 215
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)

  cdata = request.add_tlv(TLV_TYPE_CHANNEL_DATA, buf)
  if( ( self.flags & CHANNEL_FLAG_COMPRESS ) == CHANNEL_FLAG_COMPRESS )
    cdata.compress = true
  end

  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
close(addends = nil) click to toggle source

Wrapper around the low-level close.

# File lib/rex/post/meterpreter/channel.rb, line 251
def close(addends = nil)
  return _close(addends)
end
close_read() click to toggle source

Close the channel for future reads.

# File lib/rex/post/meterpreter/channel.rb, line 265
def close_read
  return _close
end
close_write() click to toggle source

Close the channel for future writes.

# File lib/rex/post/meterpreter/channel.rb, line 258
def close_write
  return _close
end
dio_close_handler(packet) click to toggle source

Stub close handler.

# File lib/rex/post/meterpreter/channel.rb, line 360
def dio_close_handler(packet)
  client.remove_channel(self.cid)

  # Trap IOErrors as parts of the channel may have already been closed
  begin
    self.cleanup
  rescue IOError
  end

  # No more channel action, foo.
  self.cid = nil

  return true
end
dio_handler(dio, packet) click to toggle source

Handles dispatching I/O requests based on the request packet. The default implementation does nothing with direct I/O requests.

# File lib/rex/post/meterpreter/channel.rb, line 328
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
dio_map(method) click to toggle source

Maps packet request methods to DIO request identifiers on a per-instance basis as other instances may add custom dio handlers.

# File lib/rex/post/meterpreter/channel.rb, line 380
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
dio_read_handler(packet, length) click to toggle source

Stub read handler.

# File lib/rex/post/meterpreter/channel.rb, line 346
def dio_read_handler(packet, length)
  return true
end
dio_write_handler(packet, data) click to toggle source

Stub write handler.

# File lib/rex/post/meterpreter/channel.rb, line 353
def dio_write_handler(packet, data)
  return true
end
flag?(flag) click to toggle source

Checks to see if a flag is set on the instance's flags attribute.

# File lib/rex/post/meterpreter/channel.rb, line 401
def flag?(flag)
  return ((self.flags & flag) == flag)
end
interactive(tf = true, addends = nil) click to toggle source

Enables or disables interactive mode.

# File lib/rex/post/meterpreter/channel.rb, line 301
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
read(length = nil, addends = nil) click to toggle source

Wrapper around the low-level channel read operation.

# File lib/rex/post/meterpreter/channel.rb, line 161
def read(length = nil, addends = nil)
  return _read(length, addends)
end
synchronous?() click to toggle source

Returns whether or not the channel is operating synchronously.

# File lib/rex/post/meterpreter/channel.rb, line 408
def synchronous?
  return (self.flags & CHANNEL_FLAG_SYNCHRONOUS)
end
write(buf, length = nil, addends = nil) click to toggle source

Wrapper around the low-level write.

# File lib/rex/post/meterpreter/channel.rb, line 208
def write(buf, length = nil, addends = nil)
  return _write(buf, length, addends)
end

Protected Instance Methods

cleanup() click to toggle source

Cleans up any lingering resources

# File lib/rex/post/meterpreter/channel.rb, line 444
def cleanup
end