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

#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.


1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
# File 'lib/rex/post/meterpreter/packet.rb', line 1225

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


1126
1127
1128
# File 'lib/rex/post/meterpreter/packet.rb', line 1126

def created_at
  @created_at
end

#encrypt_flagsObject

Returns the value of attribute encrypt_flags


1129
1130
1131
# File 'lib/rex/post/meterpreter/packet.rb', line 1129

def encrypt_flags
  @encrypt_flags
end

#lengthObject

Returns the value of attribute length


1130
1131
1132
# File 'lib/rex/post/meterpreter/packet.rb', line 1130

def length
  @length
end

#rawObject

Returns the value of attribute raw


1127
1128
1129
# File 'lib/rex/post/meterpreter/packet.rb', line 1127

def raw
  @raw
end

#session_guidObject

Returns the value of attribute session_guid


1128
1129
1130
# File 'lib/rex/post/meterpreter/packet.rb', line 1128

def session_guid
  @session_guid
end

Class Method Details

.create_request(method = nil) ⇒ Object

Creates a request with the supplied method.


1181
1182
1183
# File 'lib/rex/post/meterpreter/packet.rb', line 1181

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.


1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
# File 'lib/rex/post/meterpreter/packet.rb', line 1188

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


1246
1247
1248
# File 'lib/rex/post/meterpreter/packet.rb', line 1246

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

#aes_decrypt(key, iv, data) ⇒ Object

Raises:


1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
# File 'lib/rex/post/meterpreter/packet.rb', line 1283

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:


1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
# File 'lib/rex/post/meterpreter/packet.rb', line 1266

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.


1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
# File 'lib/rex/post/meterpreter/packet.rb', line 1327

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.


1351
1352
1353
1354
1355
1356
1357
# File 'lib/rex/post/meterpreter/packet.rb', line 1351

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.


1410
1411
1412
# File 'lib/rex/post/meterpreter/packet.rb', line 1410

def method
  get_tlv_value(TLV_TYPE_COMMAND_ID)
end

#method=(method) ⇒ Object

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

Raises:


1402
1403
1404
1405
# File 'lib/rex/post/meterpreter/packet.rb', line 1402

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)

1395
1396
1397
# File 'lib/rex/post/meterpreter/packet.rb', line 1395

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

#parse_header!Object


1339
1340
1341
1342
1343
# File 'lib/rex/post/meterpreter/packet.rb', line 1339

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


1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
# File 'lib/rex/post/meterpreter/packet.rb', line 1250

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)

1382
1383
1384
# File 'lib/rex/post/meterpreter/packet.rb', line 1382

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

#resultObject

Gets the value of the packet's result TLV.


1432
1433
1434
# File 'lib/rex/post/meterpreter/packet.rb', line 1432

def result
  get_tlv_value(TLV_TYPE_RESULT)
end

#result=(result) ⇒ Object

Sets the packet's result TLV.


1425
1426
1427
# File 'lib/rex/post/meterpreter/packet.rb', line 1425

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)

1418
1419
1420
# File 'lib/rex/post/meterpreter/packet.rb', line 1418

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

#ridObject

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


1439
1440
1441
# File 'lib/rex/post/meterpreter/packet.rb', line 1439

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.


1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
# File 'lib/rex/post/meterpreter/packet.rb', line 1305

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_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.


1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
# File 'lib/rex/post/meterpreter/packet.rb', line 1362

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