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 bytesPacket
type - 4 bytesPacket
data - X bytesIf 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
Public Class Methods
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
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
Initializes the packet to the supplied packet type and method, if any. If the packet is a request, a request identifier is created.
Metasploit::Aggregator::Tlv::GroupTlv::new
# 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
# File lib/metasploit/aggregator/tlv/packet.rb, line 710 def add_raw(bytes) self.raw << bytes end
# 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
# 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 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
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.
Metasploit::Aggregator::Tlv::GroupTlv#from_r
# 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
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
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
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
# 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
# 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
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
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
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
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
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
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 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