Class: Rex::Post::Meterpreter::Packet

Inherits:
GroupTlv show all
Defined in:
lib/rex/post/meterpreter/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 than 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
ENC_FLAG_AES128 =
0x2

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

#_tlv_type_string, #htonq, #inspect, #meta_type?, #ntohq, #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.



900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
# File 'lib/rex/post/meterpreter/packet.rb', line 900

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.



797
798
799
# File 'lib/rex/post/meterpreter/packet.rb', line 797

def created_at
  @created_at
end

#encrypt_flagsObject

Returns the value of attribute encrypt_flags.



800
801
802
# File 'lib/rex/post/meterpreter/packet.rb', line 800

def encrypt_flags
  @encrypt_flags
end

#lengthObject

Returns the value of attribute length.



801
802
803
# File 'lib/rex/post/meterpreter/packet.rb', line 801

def length
  @length
end

#rawObject

Returns the value of attribute raw.



798
799
800
# File 'lib/rex/post/meterpreter/packet.rb', line 798

def raw
  @raw
end

#session_guidObject

Returns the value of attribute session_guid.



799
800
801
# File 'lib/rex/post/meterpreter/packet.rb', line 799

def session_guid
  @session_guid
end

Class Method Details

.create_configObject

Factory



849
850
851
# File 'lib/rex/post/meterpreter/packet.rb', line 849

def Packet.create_config()
  Packet.new(PACKET_TYPE_CONFIG)
end

.create_request(method = nil) ⇒ Object

Creates a request with the supplied method.



856
857
858
# File 'lib/rex/post/meterpreter/packet.rb', line 856

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.



863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
# File 'lib/rex/post/meterpreter/packet.rb', line 863

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



921
922
923
# File 'lib/rex/post/meterpreter/packet.rb', line 921

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

#aes_decrypt(key, iv, data) ⇒ Object

Raises:

  • (ArgumentError)


958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
# File 'lib/rex/post/meterpreter/packet.rb', line 958

def aes_decrypt(key, iv, data)
  size = key.length * 8
  raise ArgumentError.new('AES key width must be 128 or 256 bits') unless (size == 128 || size == 256)
  # Create the required cipher instance
  aes = OpenSSL::Cipher.new("AES-#{size}-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

Raises:

  • (ArgumentError)


941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
# File 'lib/rex/post/meterpreter/packet.rb', line 941

def aes_encrypt(key, data)
  size = key.length * 8
  raise ArgumentError.new('AES key width must be 128 or 256 bits') unless (size == 128 || size == 256)
  # Create the required cipher instance
  aes = OpenSSL::Cipher.new("AES-#{size}-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.



1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
# File 'lib/rex/post/meterpreter/packet.rb', line 1002

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 == key[:type] && (encrypt_flags == ENC_FLAG_AES128 || encrypt_flags == ENC_FLAG_AES256)
    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.



1026
1027
1028
1029
1030
1031
1032
# File 'lib/rex/post/meterpreter/packet.rb', line 1026

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.



1085
1086
1087
# File 'lib/rex/post/meterpreter/packet.rb', line 1085

def method
  get_tlv_value(TLV_TYPE_COMMAND_ID)
end

#method=(method) ⇒ Object

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

Raises:

  • (ArgumentError)


1077
1078
1079
1080
# File 'lib/rex/post/meterpreter/packet.rb', line 1077

def method=(method)
  raise ArgumentError.new("Packet.method must be an integer. Current value is #{method}") unless method.is_a?(Integer)
  add_tlv(TLV_TYPE_COMMAND_ID, method, true)
end

#method?(method) ⇒ Boolean

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

Returns:

  • (Boolean)


1070
1071
1072
# File 'lib/rex/post/meterpreter/packet.rb', line 1070

def method?(method)
  (get_tlv_value(TLV_TYPE_COMMAND_ID) == method)
end

#parse_header!Object



1014
1015
1016
1017
1018
# File 'lib/rex/post/meterpreter/packet.rb', line 1014

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



925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
# File 'lib/rex/post/meterpreter/packet.rb', line 925

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)


1057
1058
1059
# File 'lib/rex/post/meterpreter/packet.rb', line 1057

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

#resultObject

Gets the value of the packet’s result TLV.



1107
1108
1109
# File 'lib/rex/post/meterpreter/packet.rb', line 1107

def result
  get_tlv_value(TLV_TYPE_RESULT)
end

#result=(result) ⇒ Object

Sets the packet’s result TLV.



1100
1101
1102
# File 'lib/rex/post/meterpreter/packet.rb', line 1100

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)


1093
1094
1095
# File 'lib/rex/post/meterpreter/packet.rb', line 1093

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

#ridObject

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



1114
1115
1116
# File 'lib/rex/post/meterpreter/packet.rb', line 1114

def rid
  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.



980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
# File 'lib/rex/post/meterpreter/packet.rb', line 980

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 @type != PACKET_TYPE_CONFIG && key && key[:key] && (key[:type] == ENC_FLAG_AES128 || 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 << [key[:type], 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.



1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
# File 'lib/rex/post/meterpreter/packet.rb', line 1037

def xor_bytes(xor_key, bytes)
  xor_key = xor_key.bytes
  result = ''
  i = 0
  bytes.each_byte do |b|
    result << (b ^ xor_key[i % xor_key.length]).chr
    i += 1
  end
  result
end