def __init__(self, version, ciphers_dict): self.cipher_suites = ciphers_dict self.extension_list = [ ] # ToDo: make a dict to prevent duplicate extensions try: if Protocol.is_ssl3_tls(version): if Protocol.is_tls1_3(version): # TLS1.3 uses 'legacy version' TLS1.2 for the record layer super(self.__class__, self).__init__(versions['TLSv1_2'], ContentType.handshake) self.handshake_version = versions['TLSv1_2'] else: # Record version set to TLSv1_0 super(self.__class__, self).__init__(versions['TLSv1_0'], ContentType.handshake) self.handshake_version = version self.compression = b'\x00' self.length = len(self.cipher_spec) + len( self.compression) + 42 # 32 random + 4 length + .. self._set_tls_hello_body_bytes() elif Protocol.is_ssl2(version): super(self.__class__, self).__init__(version, HandshakeTypeSsl2.client_hello) self.challenge = os.urandom(16) record_length = len(self.cipher_spec) + len(self.challenge) + 9 self.length = RecordHelper.get_ssl2_record_len( record_length, True) # Set MSB (no padding) self._set_ssl2_hello_body_bytes() except: raise Exception("Failed to craft ClientHello")
def send_client_hello(self, version, ciphers_tls=ciphers_tls): response = None with TCP(self.target.host, self.target.port).connect() as tcp: if self.clear_text_layer: stls = StartTLS(self.clear_text_layer) stls.prepare_socket(tcp) tls = TLSConnection( tcp ) # Pass the socket object (connection) to start a TLSConnection instance if Protocol.is_tls1_3(versions[version]): # TLS1.3 should ignore ciphers not supported so we SHOULD be able to provide all TLS ciphers we know client_hello = self.create_tls13_extended_client_hello( ciphers_tls) response = tls.send_record(client_hello) elif Protocol.is_ssl3_tls(versions[version]): client_hello = self.create_ecc_extended_client_hello( versions[version], ciphers_tls) client_hello.set_compression( bytearray(b'\x01\x00')) # DEFLATE, null response = tls.send_record(client_hello) elif Protocol.is_ssl2(versions[version]): response = tls.send_record( ClientHello(versions[version], ciphers_ssl2)) return response
def get_bytes(self): record_parts = [] if Protocol.is_ssl3_tls(self.version): record_parts = [ # TLS/SSLv3 record: TYPE, LENGTH, VERSION, BODY (content) bytes([self.content_type]), bytes(self.version), struct.pack("!H", self.length), self.body ] elif Protocol.is_ssl2( self.version ) and self.content_type == HandshakeTypeSsl2.client_hello: record_parts = [ # SSLv2 record : LENGTH, TYPE, VERSION, BODY (content) struct.pack("!H", self.length), bytes([self.content_type]), bytes(self.version), # tuple -> bytes self.body ] # ToDo raise exceptions elif Protocol.is_ssl2( self.version ) and self.content_type == HandshakeTypeSsl2.server_hello: print( "The byte representation of SSL2 server_hello is not implemented" ) else: print("Byte representation of RecordType not implemented") return b''.join(record_parts)
def get_version_support(self, version_list): supported = [] self.print_verbose( "Enumerating TLS/SSL version support for: {0} port {1}".format( self.target.host, self.target.port)) for v in version_list: # try: with TCP(self.target.host, self.target.port).connect() as tcp: if self.clear_text_layer: stls = StartTLS(self.clear_text_layer) stls.prepare_socket(tcp) tls = TLSConnection( tcp ) # Pass the socket object (connection) to start a TLSConnection instance if Protocol.is_tls1_3(versions[v]): # TLS1.3 should ignore ciphers not supported so we SHOULD be able to provide all TLS ciphers we know client_hello = self.get_tls13_extended_client_hello( ciphers_tls) response = tls.send_record(client_hello) elif Protocol.is_ssl3_tls(versions[v]): client_hello = self.get_ecc_extended_client_hello( versions[v], ciphers_tls) client_hello.set_compression( bytearray(b'\x01\x00')) # DEFLATE, null response = tls.send_record(client_hello) elif Protocol.is_ssl2(versions[v]): response = tls.send_record( ClientHello(versions[v], ciphers_ssl2)) if len( response ) > 0: # ToDo: response may be referenced before assignment -> fix! s_hello = None # The ServerHello may not be the first Record received for record in response: if isinstance(record, ServerHello): s_hello = record break if s_hello: if s_hello.handshake_protocol == versions[v]: supported.append(v) self.print_verbose(" [+]: {0}".format(v)) if s_hello.compression_method is not None: self.print_verbose( " Compression: {0}".format( s_hello.compression_method.name)) if Protocol.is_tls1_3( versions[v] ): # Need to see if extension is present for extension in s_hello.extensions(): if extension.extension_type == ExtensionType.supported_versions: supported.append(v) self.print_verbose(" [+]: {0}".format(v)) # except AttributeError: # break # except: # raise return supported
def __init__(self, version, body): if Protocol.is_ssl3_tls(version): super(self.__class__, self).__init__(version, ContentType.handshake) elif Protocol.is_ssl2(version): super(self.__class__, self).__init__(version, HandshakeTypeSsl2.server_hello) else: raise TypeError("Protocol unsupported") # ToDo TLSException self.handshake_type = body[0] # Check if applicable for SSL2 self.length = len(body) self.body = body
def get_version_support(self, version_list): supported = [] if self.sni: self.print_verbose(" [*] Using SNI: '{}'".format(self.sni_name)) else: self.print_verbose(" [*] SNI extension disabled.") self.print_verbose( "Enumerating TLS/SSL protocol version support for: {0} port {1}". format(self.target.host, self.target.port)) for v in version_list: response = self.send_client_hello(v) if len(response) > 0: s_hello = None for record in response: if isinstance(record, ServerHello): s_hello = record break if s_hello: if s_hello.handshake_protocol == versions[v]: supported.append(v) self.print_verbose(" [+] {0}".format(v)) if s_hello.compression_method is not None: self.print_verbose(" Compression: {0}".format( s_hello.compression_method.name)) if Protocol.is_tls1_3(versions[v]) and \ s_hello.extensions_length > 0: # Check if relevant extension is present for extension in s_hello.extensions(): if extension.extension_type == ExtensionType.supported_versions: supported.append(v) self.print_verbose(" [+] {0}".format(v)) return supported
def get_version_support(self, version_list): supported = [] if self.sni: self.print_verbose(" [*] Using SNI: '{}'".format(self.sni_name)) else: self.print_verbose(" [*] SNI extension disabled.") self.print_verbose("Enumerating TLS/SSL protocol version support for: {0} port {1}" .format(self.target.host, self.target.port)) for v in version_list: response = self.send_client_hello(v) if len(response) > 0: s_hello = None for record in response: if isinstance(record, ServerHello): s_hello = record break if s_hello: if s_hello.handshake_protocol == versions[v]: supported.append(v) self.print_verbose(" [+] {0}".format(v)) if s_hello.compression_method is not None: self.print_verbose(" Compression: {0}".format(s_hello.compression_method.name)) if Protocol.is_tls1_3(versions[v]) and \ s_hello.extensions_length > 0: # Check if relevant extension is present for extension in s_hello.extensions(): if extension.extension_type == ExtensionType.supported_versions: supported.append(v) self.print_verbose(" [+] {0}".format(v)) return supported
def __init__(self, version, body): if Protocol.is_ssl3_tls(version): super(self.__class__, self).__init__(version, ContentType.handshake) self.handshake_type = body[0] self.length = len(body) self.body = body
def get_response_record(self, record): response = record buffer = self.TCP.receive_buffer(record.length) # obtain the whole record if buffer: record.body = buffer if Protocol.is_ssl3_tls(record.version): if record.content_type == ContentType.handshake: msg_start = 0 if record.body[0] == HandshakeType.server_hello: response = ServerHello(record.version, record.body) self.kex_algorithm = response.kex_algorithm() # Workaround/hack for multiple messages inside the record_layer while record.length > msg_start: message = None msg_len = struct.unpack('!I', b'\x00' + record.body[msg_start + 1:msg_start + 4])[0] if record.body[msg_start] == HandshakeType.server_hello: message = ServerHello(record.version, record.body[msg_start:msg_start+msg_len]) self.kex_algorithm = response.kex_algorithm() # set the key exchange algo for the connection elif record.body[msg_start] == HandshakeType.certificate: message = Certificate(record.version, record.body[msg_start:msg_start+msg_len]) elif record.body[msg_start] == HandshakeType.server_key_exchange: message = ServerKeyExchange(record.version, record.body[msg_start:msg_start+msg_len], self.kex_algorithm) if message: response.messages.append(message) # (handshake_type + length_field) 4 bytes msg_start = msg_start + 4 + msg_len elif record.body[0] == HandshakeType.certificate: response = Certificate(record.version, record.body) elif record.body[0] == HandshakeType.server_key_exchange: response = ServerKeyExchange(record.version, record.body, self.kex_algorithm) elif record.content_type == ContentType.alert: self.print_verbose("Received an alert!") # TODO: return alert object else: self.print_verbose("Unhandled response for TLS request record") elif Protocol.is_ssl2(record.version): if record.content_type == HandshakeTypeSsl2.server_hello: # server hello version = Protocol.version_from_bytes(response.body[2:4]) # For SSL2 version is part of the 'body' response = ServerHello(version, record.body) response.length = record.length else: self.print_verbose("Unhandled response for SSL2 request record") else: self.print_verbose("No body received") return response
def send_record(self, record): # TODO record instance response = [] # Create a list of record objects if not isinstance(record, Record): raise Exception( "send_record (TLS) was not passed a Record instance") try: self.TCP.send_all(record.get_bytes( )) # Sending the byte representation of the object if Protocol.is_ssl3_tls(record.version): # TLS/SSL header = self.get_header(5) # TYPE(1), VERSION(2), LENGTH(2) while header: if header and len(header) == 5: rec = Record(Protocol.version_from_bytes(header[1:3]), struct.unpack('!B', header[0:1])[0]) rec.length = struct.unpack('!H', header[3:5])[0] if 0 < rec.length: response.append(self.get_response_record(rec)) next_header = self.TCP.receive_buffer(5) if next_header: header = next_header del next_header else: break elif Protocol.is_ssl2(record.version): header = self.get_header(3) # LENGTH(2), TYPE(1) if header and len(header) == 3: rec = Record( record.version, struct.unpack('!B', header[2:3])[0]) # Version is assumed rec.length = RecordHelper.get_ssl2_record_len( struct.unpack('!H', header[0:2])[0] - 3) if 0 < rec.length: response.append(self.get_response_record(rec)) except socket.error as e: # ToDo raise exception if e.errno == errno.ECONNRESET: # 54; Microsoft sometimes just resets the connection msg = "Connection reset" # Usually means: not supported or not an acceptable offer pass elif e.errno == errno.ECONNREFUSED: # 61 msg = "Connection refused" else: raise e # print(msg) return response
def add_extension(self, extension): # ToDo check if extension_type is already present in hello if Protocol.is_ssl3_tls(self.version) and isinstance(extension, Extension): try: self.extension_list.append(extension) self._set_tls_hello_body_bytes() # We need to update the hello_body except: raise Exception("Something went wrong adding extension type: {0}".format(extension.extension_type)) else: raise Exception("Cannot add extension to the protocol!")
def send_client_hello(self, version, ciphers_tls=ciphers_tls): response = None with TCP(self.target.host, self.target.port).connect() as tcp: if self.clear_text_layer: stls = StartTLS(self.clear_text_layer) stls.prepare_socket(tcp) tls = TLSConnection(tcp) # Pass the socket object (connection) to start a TLSConnection instance if Protocol.is_tls1_3(versions[version]): # TLS1.3 should ignore ciphers not supported so we SHOULD be able to provide all TLS ciphers we know client_hello = self.create_tls13_extended_client_hello(ciphers_tls) response = tls.send_record(client_hello) elif Protocol.is_ssl3_tls(versions[version]): client_hello = self.create_ecc_extended_client_hello(versions[version], ciphers_tls) client_hello.set_compression(bytearray(b'\x01\x00')) # DEFLATE, null response = tls.send_record(client_hello) elif Protocol.is_ssl2(versions[version]): response = tls.send_record(ClientHello(versions[version], ciphers_ssl2)) return response
def send_record(self, record): # TODO record instance response = [] # Create a list of record objects if not isinstance(record, Record): raise Exception("send_record (TLS) was not passed a Record instance") try: self.TCP.send_all(record.get_bytes()) # Sending the byte representation of the object if Protocol.is_ssl3_tls(record.version): # TLS/SSL header = self.get_header(5) # TYPE(1), VERSION(2), LENGTH(2) while header: if header and len(header) == 5: rec = Record(Protocol.version_from_bytes(header[1:3]), struct.unpack('!B', header[0:1])[0]) rec.length = struct.unpack('!H', header[3:5])[0] if 0 < rec.length: response.append(self.get_response_record(rec)) next_header = self.TCP.receive_buffer(5) if next_header: header = next_header del next_header else: break elif Protocol.is_ssl2(record.version): header = self.get_header(3) # LENGTH(2), TYPE(1) if header and len(header) == 3: rec = Record(record.version, struct.unpack('!B', header[2:3])[0]) # Version is assumed rec.length = RecordHelper.get_ssl2_record_len(struct.unpack('!H', header[0:2])[0] - 3) if 0 < rec.length: response.append(self.get_response_record(rec)) except socket.error as e: # ToDo raise exception if e.errno == errno.ECONNRESET: # 54; Microsoft sometimes just resets the connection msg = "Connection reset" # Usually means: not supported or not an acceptable offer pass elif e.errno == errno.ECONNREFUSED: # 61 msg = "Connection refused" else: raise e # print(msg) return response
def get_response_record(self, record): response = record buffer = self.TCP.receive_buffer(record.length) if buffer: record.body = buffer if Protocol.is_ssl3_tls(record.version): if record.content_type == ContentType.handshake: if record.body[0] == HandshakeType.server_hello: response = ServerHello(record.version, record.body) self.kex_algorithm = response.kex_algorithm() elif record.body[0] == HandshakeType.certificate: response = Certificate(record.version, record.body) elif record.body[0] == HandshakeType.server_key_exchange: # print("got server kex pre") response = ServerKeyExchange(record.version, record.body, self.kex_algorithm) # print("got server kex post") elif record.content_type == ContentType.alert: self.print_verbose( "Received an alert!") # TODO: return alert object else: self.print_verbose( "Unhandled response for TLS request record") elif Protocol.is_ssl2(record.version): if record.content_type == HandshakeTypeSsl2.server_hello: # server hello version = Protocol.version_from_bytes( response.body[2:4] ) # For SSL2 version is part of the 'body' response = ServerHello(version, record.body) response.length = record.length else: self.print_verbose( "Unhandled response for SSL2 request record") else: self.print_verbose("No body received") return response
def __init__(self, version, body, key_exchange_algorithm): if Protocol.is_ssl3_tls(version): super(self.__class__, self).__init__(version, ContentType.handshake) self.handshake_type = body[0] # ToDo DRY-up (other classes seem to do the same) self.length = len(body) self.body = body self.key_exchange_algorithm = key_exchange_algorithm self.key_length() if 'ecdhe' in key_exchange_algorithm.name: self.curve_type = self.body[4] self.elliptic = True for curve in NamedCurve: if curve.value == struct.unpack('!H', self.body[5:7])[0]: self.named_curve = curve break
def get_cipher_support(self, version): supported = [] retries = 0 cipher_list = None if Protocol.is_ssl3_tls(versions[version]): cipher_list = list(ciphers_tls) elif Protocol.is_ssl2(versions[version]): cipher_list = list(ciphers_ssl2) server_hello_cipher = True self.print_verbose("Enumerating supported ciphers for: {0}".format(version)) while server_hello_cipher: for _ in cipher_list: try: with TCP(self.target.host, self.target.port).connect() as tcp: if self.clear_text_layer: stls = StartTLS(self.clear_text_layer) stls.prepare_socket(tcp) tls = TLSConnection(tcp) if Protocol.is_ssl3_tls(versions[version]): if versions[version] == versions[Protocol.TLS1_3]: response = tls.send_record(self.create_tls13_extended_client_hello(cipher_list)) else: response = tls.send_record(self.create_ecc_extended_client_hello(versions[version], cipher_list)) if len(response) > 0: s_hello = None s_key_exchange = None for record in response: if isinstance(record, ServerHello): s_hello = record elif isinstance(record, ServerKeyExchange): s_key_exchange = record for message in record.messages: if isinstance(message, ServerKeyExchange): s_key_exchange = message break if s_hello: hello_cipher = s_hello.response_cipher if hello_cipher and hello_cipher in supported: server_hello_cipher = False break elif hello_cipher: supported.append(hello_cipher) self.print_cipher(hello_cipher, s_key_exchange) cipher_list.remove(hello_cipher.bytes) retries = 0 else: # No hello received, could be an alert server_hello_cipher = False break else: # Bug-fix if retries < 3: retries += 1 else: server_hello_cipher = False break elif Protocol.is_ssl2(versions[version]): response = tls.send_record(ClientHello(versions[version], cipher_list)) if len(response) > 0: if isinstance(response[0], ServerHello): supported = response[0].ssl2_response_ciphers # ssl2 returns all ciphers at once [self.print_verbose(" [+] {0}".format(s[1])) for s in supported] server_hello_cipher = False break else: server_hello_cipher = False break except AttributeError: break except: raise return supported
def get_cipher_support(self, version): supported = [] retries = 0 cipher_list = None if Protocol.is_ssl3_tls(versions[version]): cipher_list = list(ciphers_tls) elif Protocol.is_ssl2(versions[version]): cipher_list = list(ciphers_ssl2) server_hello_cipher = True self.print_verbose( "Enumerating supported ciphers for: {0}".format(version)) while server_hello_cipher: for _ in cipher_list: try: with TCP(self.target.host, self.target.port).connect() as tcp: if self.clear_text_layer: stls = StartTLS(self.clear_text_layer) stls.prepare_socket(tcp) tls = TLSConnection(tcp) if Protocol.is_ssl3_tls(versions[version]): if versions[version] == versions[Protocol.TLS1_3]: response = tls.send_record( self.create_tls13_extended_client_hello( cipher_list)) else: response = tls.send_record( self.create_ecc_extended_client_hello( versions[version], cipher_list)) if len(response) > 0: s_hello = None s_key_exchange = None for record in response: if isinstance(record, ServerHello): s_hello = record elif isinstance(record, ServerKeyExchange): s_key_exchange = record for message in record.messages: if isinstance(message, ServerKeyExchange): s_key_exchange = message break if s_hello: hello_cipher = s_hello.response_cipher if hello_cipher and hello_cipher in supported: server_hello_cipher = False break elif hello_cipher: supported.append(hello_cipher) self.print_cipher( hello_cipher, s_key_exchange) cipher_list.remove(hello_cipher.bytes) retries = 0 else: # No hello received, could be an alert server_hello_cipher = False break else: # Bug-fix if retries < 3: retries += 1 else: server_hello_cipher = False break elif Protocol.is_ssl2(versions[version]): response = tls.send_record( ClientHello(versions[version], cipher_list)) if len(response) > 0: if isinstance(response[0], ServerHello): supported = response[ 0].ssl2_response_ciphers # ssl2 returns all ciphers at once [ self.print_verbose(" [+] {0}".format( s[1])) for s in supported ] server_hello_cipher = False break else: server_hello_cipher = False break except AttributeError: break except: raise return supported
def get_cipher_support(self, version): supported = [] retries = 0 cipher_list = None if Protocol.is_ssl3_tls(versions[version]): cipher_list = list( ciphers_tls) # TLS.get_cipher_list(TLS.Protocols.TLS) elif Protocol.is_ssl2(versions[version]): cipher_list = list(ciphers_ssl2) server_hello_cipher = True self.print_verbose("Enumerating ciphers for: {0}".format(version)) while server_hello_cipher: for c in cipher_list: try: with TCP(self.target.host, self.target.port).connect() as tcp: if self.clear_text_layer: stls = StartTLS(self.clear_text_layer) stls.prepare_socket(tcp) tls = TLSConnection(tcp) if Protocol.is_ssl3_tls(versions[version]): if versions[version] == versions[Protocol.TLS1_3]: response = tls.send_record( self.get_tls13_extended_client_hello( cipher_list)) else: response = tls.send_record( self.get_ecc_extended_client_hello( versions[version], cipher_list)) if len(response) > 0: s_hello = None s_key_exchange = None for record in response: if isinstance(record, ServerHello): s_hello = record elif isinstance(record, ServerKeyExchange): s_key_exchange = record if s_hello: hello_cipher = s_hello.response_cipher if hello_cipher and hello_cipher in supported: server_hello_cipher = False break elif hello_cipher: # Should we check if the response is using TLSv1.3? '''if versions[version] == versions[Protocol.TLS1_3]: for extension in s_hello.extensions(): if extension.extension_type == ExtensionType.supported_versions: break ''' supported.append(hello_cipher) # This is ugly but hey it's not the only thing... if s_key_exchange: self.print_verbose( " [+] {0} ({1} bits) - {dh}{curve}{bits}" .format( hello_cipher.name, hello_cipher.bits, bits="{} bits".format( s_key_exchange. key_length() * 8) if not s_key_exchange.elliptic else "", dh="ECDH" if s_key_exchange.elliptic else "DH", curve=" {} ".format( s_key_exchange. named_curve.name) if s_key_exchange.elliptic else " ")) else: self.print_verbose( " [+] {0} ({1} bits)".format( hello_cipher.name, hello_cipher.bits)) cipher_list.remove(hello_cipher.bytes) retries = 0 else: # No hello received, could be an alert server_hello_cipher = False break else: # Bug-fix if retries < 3: retries += 1 else: server_hello_cipher = False break elif Protocol.is_ssl2(versions[version]): response = tls.send_record( ClientHello(versions[version], cipher_list)) if len(response) > 0: if isinstance(response[0], ServerHello): supported = response[ 0].ssl2_response_ciphers # ssl2 returns all ciphers at once if self.verbose: [ print(" [+] {0}".format(s[1])) for s in supported ] server_hello_cipher = False break else: server_hello_cipher = False break except AttributeError: break except: raise return supported
def get_response_record(self, record): response = record buffer = self.TCP.receive_buffer( record.length) # obtain the whole record if buffer: record.body = buffer if Protocol.is_ssl3_tls(record.version): if record.content_type == ContentType.handshake: msg_start = 0 if record.body[0] == HandshakeType.server_hello: response = ServerHello(record.version, record.body) self.kex_algorithm = response.kex_algorithm() # Workaround/hack for multiple messages inside the record_layer while record.length > msg_start: message = None msg_len = struct.unpack( '!I', b'\x00' + record.body[msg_start + 1:msg_start + 4])[0] if record.body[ msg_start] == HandshakeType.server_hello: message = ServerHello( record.version, record.body[msg_start:msg_start + msg_len]) self.kex_algorithm = response.kex_algorithm( ) # set the key exchange algo for the connection elif record.body[ msg_start] == HandshakeType.certificate: message = Certificate( record.version, record.body[msg_start:msg_start + msg_len]) elif record.body[ msg_start] == HandshakeType.server_key_exchange: message = ServerKeyExchange( record.version, record.body[msg_start:msg_start + msg_len], self.kex_algorithm) if message: response.messages.append(message) # (handshake_type + length_field) 4 bytes msg_start = msg_start + 4 + msg_len elif record.body[0] == HandshakeType.certificate: response = Certificate(record.version, record.body) elif record.body[0] == HandshakeType.server_key_exchange: response = ServerKeyExchange(record.version, record.body, self.kex_algorithm) elif record.content_type == ContentType.alert: self.print_verbose( "Received an alert!") # TODO: return alert object else: self.print_verbose( "Unhandled response for TLS request record") elif Protocol.is_ssl2(record.version): if record.content_type == HandshakeTypeSsl2.server_hello: # server hello version = Protocol.version_from_bytes( response.body[2:4] ) # For SSL2 version is part of the 'body' response = ServerHello(version, record.body) response.length = record.length else: self.print_verbose( "Unhandled response for SSL2 request record") else: self.print_verbose("No body received") return response
def set_compression(self, compression: bytearray): if Protocol.is_ssl3_tls(self.version): self.compression = compression self._set_tls_hello_body_bytes()
def handshake_protocol(self): if Protocol.is_ssl3_tls(self.version): version = Protocol.version_from_bytes(self.body[4:6]) else: version = self.version return version
def compression_method(self): if Protocol.is_ssl3_tls(self.version): start = 39 + self.session_id_length + 2 compression = self.body[start:start + 1] return CompressionMethod(struct.unpack('!B', compression)[0])