class Metasploit::Aggregator::Tlv::Packet

The logical meterpreter packet class

Constants

AES_IV_SIZE
ENCRYPTED_FLAGS_SIZE
ENC_FLAG_AES256
ENC_FLAG_NONE
PACKET_HEADER_SIZE
PACKET_LENGTH_SIZE
PACKET_TYPE_SIZE
XOR_KEY_SIZE

The Packet container itself has a custom header that is slightly different to the typical TLV packets. The header contains the following:

XOR KEY - 4 bytes Session GUID - 16 bytes Encrypt flags - 4 bytes Packet length - 4 bytes Packet type - 4 bytes Packet data - X bytes

If the encrypt flags are zero, then the Packet data is just straight TLV values as per the normal TLV packet structure.

If the encrypt flags are non-zer, then the Packet data is encrypted based on the scheme.

Flag == 1 (AES256)

IV             - 16 bytes
Encrypted data - X bytes

The key that is required to decrypt the data is stored alongside the session data, and hence when the packet is initially parsed, only the header is accessed. The packet itself will need to be decrypted on the fly at the point that it is required and at that point the decryption key needs to be provided.

Attributes

created_at[RW]
encrypt_flags[RW]
length[RW]
raw[RW]
session_guid[RW]

Public Class Methods

create_request(method = nil) click to toggle source

Creates a request with the supplied method.

# File lib/metasploit/aggregator/tlv/packet.rb, line 645
def Packet.create_request(method = nil)
  Packet.new(PACKET_TYPE_REQUEST, method)
end
create_response(request = nil) click to toggle source

Creates a response to a request if one is provided.

# File lib/metasploit/aggregator/tlv/packet.rb, line 652
def Packet.create_response(request = nil)
  response_type = PACKET_TYPE_RESPONSE
  method = nil
  id = nil

  if (request)
    if (request.type?(PACKET_TYPE_PLAIN_REQUEST))
      response_type = PACKET_TYPE_PLAIN_RESPONSE
    end

    method = request.method

    if request.has_tlv?(TLV_TYPE_REQUEST_ID)
      id = request.get_tlv_value(TLV_TYPE_REQUEST_ID)
    end
  end

  packet = Packet.new(response_type, method)

  if id
    packet.add_tlv(TLV_TYPE_REQUEST_ID, id)
  end

  packet
end
new(type = nil, method = nil) click to toggle source

Initializes the packet to the supplied packet type and method, if any. If the packet is a request, a request identifier is created.

# File lib/metasploit/aggregator/tlv/packet.rb, line 689
def initialize(type = nil, method = nil)
  super(type)

  if method
    self.method = method
  end

  self.created_at = ::Time.now
  self.raw = ''

  # If it's a request, generate a random request identifier
  if ((type == PACKET_TYPE_REQUEST) ||
      (type == PACKET_TYPE_PLAIN_REQUEST))
    rid = ''

    32.times { |val| rid << rand(10).to_s }

    add_tlv(TLV_TYPE_REQUEST_ID, rid)
  end
end

Public Instance Methods

add_raw(bytes) click to toggle source
# File lib/metasploit/aggregator/tlv/packet.rb, line 710
def add_raw(bytes)
  self.raw << bytes
end
aes_decrypt(key, iv, data) click to toggle source
# File lib/metasploit/aggregator/tlv/packet.rb, line 745
def aes_decrypt(key, iv, data)
  # Create the required cipher instance
  aes = OpenSSL::Cipher.new('AES-256-CBC')
  # Generate a truly random IV

  # set up the encryption
  aes.decrypt
  aes.key = key
  aes.iv = iv

  # decrypt!
  aes.update(data) + aes.final
end
aes_encrypt(key, data) click to toggle source
# File lib/metasploit/aggregator/tlv/packet.rb, line 730
def aes_encrypt(key, data)
  # Create the required cipher instance
  aes = OpenSSL::Cipher.new('AES-256-CBC')
  # Generate a truly random IV
  iv = aes.random_iv

  # set up the encryption
  aes.encrypt
  aes.key = key
  aes.iv = iv

  # encrypt and return the IV along with the result
  return iv, aes.update(data) + aes.final
end
decrypt_packet(key, encrypt_flags, data) click to toggle source

Decrypt the packet based on the content of the encryption flags.

# File lib/metasploit/aggregator/tlv/packet.rb, line 787
def decrypt_packet(key, encrypt_flags, data)
  # TODO: throw an error if the expected encryption isn't the same as the given
  #       as this could be an indication of hijacking or side-channel packet addition
  #       as highlighted by Justin Steven on github.
  if key && key[:key] && key[:type] && encrypt_flags == ENC_FLAG_AES256 && encrypt_flags == key[:type]
    iv = data[0, AES_IV_SIZE]
    aes_decrypt(key[:key], iv, data[iv.length..-1])
  else
    data
  end
end
from_r(key=nil) click to toggle source

Override the function that reads from a raw byte stream so that the XORing of data is included in the process prior to passing it on to the default functionality that can parse the TLV values.

# File lib/metasploit/aggregator/tlv/packet.rb, line 811
def from_r(key=nil)
  self.parse_header!
  xor_key = self.raw.unpack('a4')[0]
  data = xor_bytes(xor_key, self.raw[PACKET_HEADER_SIZE..-1])
  raw = decrypt_packet(key, self.encrypt_flags, data)
  super([self.length, self.type, raw].pack('NNA*'))
end
method() click to toggle source

Returns the value of the packet's method TLV.

# File lib/metasploit/aggregator/tlv/packet.rb, line 867
def method
  return get_tlv_value(TLV_TYPE_METHOD)
end
method=(method) click to toggle source

Sets the packet's method TLV to the method supplied.

# File lib/metasploit/aggregator/tlv/packet.rb, line 860
def method=(method)
  add_tlv(TLV_TYPE_METHOD, method, true)
end
method?(method) click to toggle source

Checks to see if the packet's method is equal to the supplied method.

# File lib/metasploit/aggregator/tlv/packet.rb, line 853
def method?(method)
  return (get_tlv_value(TLV_TYPE_METHOD) == method)
end
parse_header!() click to toggle source
# File lib/metasploit/aggregator/tlv/packet.rb, line 799
def parse_header!
  xor_key = self.raw.unpack('a4')[0]
  data = xor_bytes(xor_key, self.raw[0..PACKET_HEADER_SIZE])
  _, self.session_guid, self.encrypt_flags, self.length, self.type = data.unpack('a4a16NNN')
end
raw_bytes_required() click to toggle source
# File lib/metasploit/aggregator/tlv/packet.rb, line 714
def raw_bytes_required
  # if we have the xor bytes and length ...
  if self.raw.length >= PACKET_HEADER_SIZE
    # return a value based on the length of the data indicated by
    # the header
    xor_key = self.raw.unpack('a4')[0]
    decoded_bytes = xor_bytes(xor_key, raw[0, PACKET_HEADER_SIZE])
    _, _, _, length, _ = decoded_bytes.unpack('a4a16NNN')
    length + PACKET_HEADER_SIZE - HEADER_SIZE - self.raw.length
  else
    # Otherwise ask for the remaining bytes for the metadata to get the packet length
    # So we can do the rest of the calculation next time
    PACKET_HEADER_SIZE - self.raw.length
  end
end
response?() click to toggle source

Checks to see if the packet is a response.

# File lib/metasploit/aggregator/tlv/packet.rb, line 839
def response?
  return ((self.type == PACKET_TYPE_RESPONSE) ||
      (self.type == PACKET_TYPE_PLAIN_RESPONSE))
end
result() click to toggle source

Gets the value of the packet's result TLV.

# File lib/metasploit/aggregator/tlv/packet.rb, line 889
def result
  return get_tlv_value(TLV_TYPE_RESULT)
end
result=(result) click to toggle source

Sets the packet's result TLV.

# File lib/metasploit/aggregator/tlv/packet.rb, line 882
def result=(result)
  add_tlv(TLV_TYPE_RESULT, result, true)
end
result?(result) click to toggle source

Checks to see if the packet's result value is equal to the supplied result.

# File lib/metasploit/aggregator/tlv/packet.rb, line 875
def result?(result)
  return (get_tlv_value(TLV_TYPE_RESULT) == result)
end
rid() click to toggle source

Gets the value of the packet's request identifier TLV.

# File lib/metasploit/aggregator/tlv/packet.rb, line 896
def rid
  return get_tlv_value(TLV_TYPE_REQUEST_ID)
end
to_r(session_guid = nil, key = nil) click to toggle source

Override the function that creates the raw byte stream for sending so that it generates an XOR key, uses it to scramble the serialized TLV content, and then returns the key plus the scrambled data as the payload.

# File lib/metasploit/aggregator/tlv/packet.rb, line 765
def to_r(session_guid = nil, key = nil)
  xor_key = (rand(254) + 1).chr + (rand(254) + 1).chr + (rand(254) + 1).chr + (rand(254) + 1).chr

  raw = (session_guid || NULL_GUID).dup
  tlv_data = GroupTlv.instance_method(:to_r).bind(self).call

  if key && key[:key] && key[:type] == ENC_FLAG_AES256
    # encrypt the data, but not include the length and type
    iv, ciphertext = aes_encrypt(key[:key], tlv_data[HEADER_SIZE..-1])
    # now manually add the length/type/iv/ciphertext
    raw << [ENC_FLAG_AES256, iv.length + ciphertext.length + HEADER_SIZE, self.type, iv, ciphertext].pack('NNNA*A*')
  else
    raw << [ENC_FLAG_NONE, tlv_data].pack('NA*')
  end

  # return the xor'd result with the key
  xor_key + xor_bytes(xor_key, raw)
end
xor_bytes(xor_key, bytes) click to toggle source

Xor a set of bytes with a given XOR key.

# File lib/metasploit/aggregator/tlv/packet.rb, line 822
def xor_bytes(xor_key, bytes)
  result = ''
  bytes.bytes.zip(xor_key.bytes.cycle).each do |b|
    result << (b[0].ord ^ b[1].ord).chr
  end
  result
end