Class: Metasploit::Aggregator::Tlv::Packet

Inherits:
GroupTlv
  • Object
show all
Defined in:
lib/metasploit/aggregator/tlv/packet.rb

Overview

The logical meterpreter packet class

Constant Summary collapse

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.

4
ENCRYPTED_FLAGS_SIZE =
4
PACKET_LENGTH_SIZE =
4
PACKET_TYPE_SIZE =
4
PACKET_HEADER_SIZE =
XOR_KEY_SIZE + GUID_SIZE + ENCRYPTED_FLAGS_SIZE + PACKET_LENGTH_SIZE + PACKET_TYPE_SIZE
AES_IV_SIZE =
16
ENC_FLAG_NONE =
0x0
ENC_FLAG_AES256 =
0x1

Constants inherited from Tlv

Tlv::HEADER_SIZE

Instance Attribute Summary collapse

Attributes inherited from GroupTlv

#tlvs

Attributes inherited from Tlv

#compress, #type, #value

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from GroupTlv

#add_tlv, #add_tlvs, #each, #each_tlv, #each_tlv_with_index, #each_with_index, #get_tlv, #get_tlv_value, #get_tlv_values, #get_tlvs, #has_tlv?, #reset

Methods inherited from Tlv

#inspect, #meta_type?, #type?, #value?

Constructor Details

#initialize(type = nil, method = nil) ⇒ Packet

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



690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 690

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

Instance Attribute Details

#created_atObject

Returns the value of attribute created_at.



592
593
594
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 592

def created_at
  @created_at
end

#encrypt_flagsObject

Returns the value of attribute encrypt_flags.



595
596
597
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 595

def encrypt_flags
  @encrypt_flags
end

#lengthObject

Returns the value of attribute length.



596
597
598
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 596

def length
  @length
end

#rawObject

Returns the value of attribute raw.



593
594
595
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 593

def raw
  @raw
end

#session_guidObject

Returns the value of attribute session_guid.



594
595
596
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 594

def session_guid
  @session_guid
end

Class Method Details

.create_request(method = nil) ⇒ Object

Creates a request with the supplied method.



646
647
648
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 646

def Packet.create_request(method = nil)
  Packet.new(PACKET_TYPE_REQUEST, method)
end

.create_response(request = nil) ⇒ Object

Creates a response to a request if one is provided.



653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 653

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

Instance Method Details

#add_raw(bytes) ⇒ Object



711
712
713
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 711

def add_raw(bytes)
  self.raw << bytes
end

#aes_decrypt(key, iv, data) ⇒ Object



746
747
748
749
750
751
752
753
754
755
756
757
758
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 746

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) ⇒ Object



731
732
733
734
735
736
737
738
739
740
741
742
743
744
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 731

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) ⇒ Object

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



788
789
790
791
792
793
794
795
796
797
798
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 788

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) ⇒ Object

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.



812
813
814
815
816
817
818
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 812

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

#methodObject

Returns the value of the packet’s method TLV.



868
869
870
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 868

def method
  return get_tlv_value(TLV_TYPE_METHOD)
end

#method=(method) ⇒ Object

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



861
862
863
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 861

def method=(method)
  add_tlv(TLV_TYPE_METHOD, method, true)
end

#method?(method) ⇒ Boolean

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

Returns:

  • (Boolean)


854
855
856
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 854

def method?(method)
  return (get_tlv_value(TLV_TYPE_METHOD) == method)
end

#parse_header!Object



800
801
802
803
804
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 800

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_requiredObject



715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 715

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?Boolean

Checks to see if the packet is a response.

Returns:

  • (Boolean)


840
841
842
843
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 840

def response?
  return ((self.type == PACKET_TYPE_RESPONSE) ||
      (self.type == PACKET_TYPE_PLAIN_RESPONSE))
end

#resultObject

Gets the value of the packet’s result TLV.



890
891
892
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 890

def result
  return get_tlv_value(TLV_TYPE_RESULT)
end

#result=(result) ⇒ Object

Sets the packet’s result TLV.



883
884
885
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 883

def result=(result)
  add_tlv(TLV_TYPE_RESULT, result, true)
end

#result?(result) ⇒ Boolean

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

Returns:

  • (Boolean)


876
877
878
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 876

def result?(result)
  return (get_tlv_value(TLV_TYPE_RESULT) == result)
end

#ridObject

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



897
898
899
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 897

def rid
  return get_tlv_value(TLV_TYPE_REQUEST_ID)
end

#to_r(session_guid = nil, key = nil) ⇒ Object

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.



766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 766

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) ⇒ Object

Xor a set of bytes with a given XOR key.



823
824
825
826
827
828
829
# File 'lib/metasploit/aggregator/tlv/packet.rb', line 823

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