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 bytesThe 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_config ⇒ Object
Factory.
-
.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
#_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_at ⇒ Object
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_flags ⇒ Object
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 |
#length ⇒ Object
Returns the value of attribute length.
801 802 803 |
# File 'lib/rex/post/meterpreter/packet.rb', line 801 def length @length end |
#raw ⇒ Object
Returns the value of attribute raw.
798 799 800 |
# File 'lib/rex/post/meterpreter/packet.rb', line 798 def raw @raw end |
#session_guid ⇒ Object
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_config ⇒ Object
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
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
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 |
#method ⇒ Object
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.
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.
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_required ⇒ Object
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.
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 |
#result ⇒ Object
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.
1093 1094 1095 |
# File 'lib/rex/post/meterpreter/packet.rb', line 1093 def result?(result) (get_tlv_value(TLV_TYPE_RESULT) == result) end |
#rid ⇒ Object
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 |