Class: Rex::Post::Meterpreter::Packet
- 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
Instance Attribute Summary collapse
-
#created_at ⇒ Object
Returns the value of attribute created_at.
-
#encrypt_flags ⇒ Object
Returns the value of attribute encrypt_flags.
-
#length ⇒ Object
Returns the value of attribute length.
-
#raw ⇒ Object
Returns the value of attribute raw.
-
#session_guid ⇒ Object
Returns the value of attribute session_guid.
Attributes inherited from GroupTlv
Attributes inherited from Tlv
Class Method Summary collapse
-
.create_request(method = nil) ⇒ Object
Creates a request with the supplied method.
-
.create_response(request = nil) ⇒ Object
Creates a response to a request if one is provided.
Instance Method Summary collapse
- #add_raw(bytes) ⇒ Object
- #aes_decrypt(key, iv, data) ⇒ Object
- #aes_encrypt(key, data) ⇒ Object
-
#decrypt_packet(key, encrypt_flags, data) ⇒ Object
Decrypt the packet based on the content of the encryption flags.
-
#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.
-
#initialize(type = nil, method = nil) ⇒ Packet
constructor
Initializes the packet to the supplied packet type and method, if any.
-
#method ⇒ Object
Returns the value of the packet's method TLV.
-
#method=(method) ⇒ Object
Sets the packet's method TLV to the method supplied.
-
#method?(method) ⇒ Boolean
Checks to see if the packet's method is equal to the supplied method.
- #parse_header! ⇒ Object
- #raw_bytes_required ⇒ Object
-
#response? ⇒ Boolean
Checks to see if the packet is a response.
-
#result ⇒ Object
Gets the value of the packet's result TLV.
-
#result=(result) ⇒ Object
Sets the packet's result TLV.
-
#result?(result) ⇒ Boolean
Checks to see if the packet's result value is equal to the supplied result.
-
#rid ⇒ Object
Gets the value of the packet's request identifier TLV.
-
#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.
-
#xor_bytes(xor_key, bytes) ⇒ Object
Xor a set of bytes with a given XOR key.
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_at ⇒ Object
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_flags ⇒ Object
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 |
#length ⇒ Object
Returns the value of attribute length
1130 1131 1132 |
# File 'lib/rex/post/meterpreter/packet.rb', line 1130 def length @length end |
#raw ⇒ Object
Returns the value of attribute raw
1127 1128 1129 |
# File 'lib/rex/post/meterpreter/packet.rb', line 1127 def raw @raw end |
#session_guid ⇒ Object
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
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
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 |
#method ⇒ Object
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.
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.
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_required ⇒ Object
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.
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 |
#result ⇒ Object
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.
1418 1419 1420 |
# File 'lib/rex/post/meterpreter/packet.rb', line 1418 def result?(result) (get_tlv_value(TLV_TYPE_RESULT) == result) end |
#rid ⇒ Object
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 |