Exemple #1
0
    def _inject_ccs(self, record, hello_message_index):
        """Inject an early CCS while preserving the rest of the data."""
        version = record.version

        ccs = TlsRecord(
            TlsRecord.CONTENT_TYPE.CHANGE_CIPHER_SPEC,
            version,
            [tls.types.ChangeCipherSpec(1)])

        rec = TlsRecord(
            TlsRecord.CONTENT_TYPE.HANDSHAKE,
            version,
            record.messages[:hello_message_index + 1])

        # Split the record if there are more messages after the ServerHello.
        remaining = record.messages[hello_message_index + 1:]
        if remaining:
            rest = TlsRecord(
                TlsRecord.CONTENT_TYPE.HANDSHAKE,
                version,
                remaining).to_bytes()
        else:
            rest = ""

        return rec.to_bytes() + ccs.to_bytes() + rest
Exemple #2
0
    def on_response(self, response):
        if not self.ssl or self.bridge or self.injected_server:
            return response
        response = self.buffer + response
        self.buffer = ""
        try:
            index = 0
            while index < len(response):
                record, size = TlsRecord.from_stream(response[index:])
                version = record.version
                for i, message in enumerate(record.messages):
                    # Inject the CCS right after the ServerHello
                    if (isinstance(message, tls.types.HandshakeMessage)
                           and message.type == HandshakeMessage.TYPE.SERVER_HELLO):
                        response = (response[:index] +
                                self._inject_ccs(record, i) +
                                response[index+size:])
                        self.injected_server = True
                        return response

                index += size

        except ValueError:
            # Failed to parse TLS, this is probably due to a short read of a TLS
            # record. Buffer the response to try and get more data.
            self.buffer = response
            # But don't buffer too much, give up after 16k.
            if len(self.buffer) > 2**14:
                response = self.buffer
                self.buffer = ""
                return self.buffer
            return ""
        return response
    def on_response(self, response):
        if not self.ssl or self.signature_tampered:
            return response
        response = self.buffer + response
        self.buffer = ""
        # Tamper with the ServerKeyExchange message.
        try:
            index = 0
            while index < len(response):
                record, size = TlsRecord.from_stream(response[index:])
                version = record.version
                for i, message in enumerate(record.messages):
                    if (isinstance(message, HandshakeMessage) and
                        (message.type
                         == HandshakeMessage.TYPE.SERVER_KEY_EXCHANGE)):
                        tampered_record_bytes = (
                            self._tamper_with_server_key_exchange(record, i))
                        response = (response[:index] + tampered_record_bytes +
                                    response[index + size:])
                        self.signature_tampered = True
                        return response

                index += size

        except ValueError:
            # Failed to parse TLS, this is probably due to a short read of a TLS
            # record. Buffer the response to try and get more data.
            self.buffer = response
            # But don't buffer too much, give up after 16k.
            if len(self.buffer) > 2**14:
                response = self.buffer
                self.buffer = ""
                return self.buffer
            return ""
        return response
Exemple #4
0
    def on_request(self, request):
        if not self.ssl or self.bridge:
            return request
        try:
            record, size = TlsRecord.from_stream(request)
            message = record.messages[0]
            if not self.clienthello_handled:
                self.clienthello_handled = True
                hello = message.obj
                # Force a full handshake by preventing session resumption by emptying
                # session ID and SessionTicket extension. Otherwise a CCS will follow
                # a ServerHello normally.
                hello.session_id = []
                for ext in hello.extension_list:
                    if ext.type == Extension.TYPE.SESSIONTICKET:
                        ext.raw_data = []
                return record.to_bytes()
            if self.injected_server:
                # OpenSSL after the EarlyCCS fix should send a fatal alert
                # unexpected_message (10). Some other libraries send a close_notify (0)
                # so we accept that as well. Morever, if the client doesn't like the TLS
                # protocol version chosen by the server (regardless of whether early
                # CCS is injected), the client will send a fatal alert
                # protocol_version (70).
                if not (
                    isinstance(message, tls.types.Alert) and
                       ((message.description == Alert.DESCRIPTION.UNEXPECTED_MESSAGE and
                           message.level == Alert.LEVEL.FATAL) or
                       (message.description == Alert.DESCRIPTION.PROTOCOL_VERSION and
                           message.level == Alert.LEVEL.FATAL) or
                       message.description == Alert.DESCRIPTION.CLOSE_NOTIFY)):
                    self.log(
                        logging.CRITICAL,
                        "Client is vulnerable to Early CCS attack!")
                    self.connection.vuln_notify(util.vuln.VULN_EARLY_CCS)
                    self.log_attack_event()

                    self.connection.close()
                else:
                    self.log(
                        logging.DEBUG,
                        "Client not vulnerable to early CCS")
                    self.log_attack_event(success=False)

        except ValueError:
            # Failed to parse TLS, this is probably due to a short read of a TLS
            # record.
            pass
        return request
    def on_request(self, request):
        if not self.ssl:
            return request
        try:
            record, size = TlsRecord.from_stream(request)
            message = record.messages[0]
            if not self.clienthello_adjusted:
                self.clienthello_adjusted = True
                hello = message.obj
                # Force a full handshake (and thus a key exchange) by preventing
                # session resumption by clearing session ID and SessionTicket.
                hello.session_id = []
                for ext in hello.extension_list:
                    if ext.type == Extension.TYPE.SESSIONTICKET:
                        ext.raw_data = []
                # Retain in ClientHello only cipher suites which require the
                # server to send a ServerKeyExchange message: emphemeral (EC)DH
                # and RSA_EXPORT cipher suites. Also retain pseudo/signalling
                # cipher suites because they don't affect this attack/test.
                hello.ciphers = [c for c in hello.ciphers
                    if ("_DHE_" in str(c) or
                       "_ECDHE_" in str(c) or
                       "_RSA_EXPORT_" in str(c) or
                       str(c).endswith("_SCSV"))]
                return record.to_bytes()
            if self.signature_tampered:
                # The client MUST reply with an alert and close the connection.
                # Just closing the connection is also acceptable.
                if not self.first_alert_received_after_tampering:
                    if isinstance(message, Alert):
                        self.first_alert_received_after_tampering = message
                        return request

                self.vuln_detected = True
                self.log(
                    logging.CRITICAL,
                    ("Client is vulnerable to server key substitution"
                    " attack! Client reply: %s" % str(message)))
                self.connection.vuln_notify(
                    util.vuln.VULN_TLS_SERVER_KEY_REPLACEMENT)
                self.log_attack_event()
                self.connection.close()
                return request

        except ValueError:
            # Failed to parse TLS, this is probably due to a short read of a TLS
            # record.
            pass
        return request
    def on_request(self, request):
        if not self.ssl:
            return request
        try:
            record, size = TlsRecord.from_stream(request)
            message = record.messages[0]
            if not self.clienthello_adjusted:
                self.clienthello_adjusted = True
                hello = message.obj
                # Force a full handshake (and thus a key exchange) by preventing
                # session resumption by clearing session ID and SessionTicket.
                hello.session_id = []
                for ext in hello.extension_list:
                    if ext.type == Extension.TYPE.SESSIONTICKET:
                        ext.raw_data = []
                # Retain in ClientHello only cipher suites which require the
                # server to send a ServerKeyExchange message: emphemeral (EC)DH
                # and RSA_EXPORT cipher suites. Also retain pseudo/signalling
                # cipher suites because they don't affect this attack/test.
                hello.ciphers = [
                    c for c in hello.ciphers
                    if ("_DHE_" in str(c) or "_ECDHE_" in str(c) or
                        "_RSA_EXPORT_" in str(c) or str(c).endswith("_SCSV"))
                ]
                return record.to_bytes()
            if self.signature_tampered:
                # The client MUST reply with an alert and close the connection.
                # Just closing the connection is also acceptable.
                if not self.first_alert_received_after_tampering:
                    if isinstance(message, Alert):
                        self.first_alert_received_after_tampering = message
                        return request

                self.vuln_detected = True
                self.log(logging.CRITICAL,
                         ("Client is vulnerable to server key substitution"
                          " attack! Client reply: %s" % str(message)))
                self.connection.vuln_notify(
                    util.vuln.VULN_TLS_SERVER_KEY_REPLACEMENT)
                self.log_attack_event()
                self.connection.close()
                return request

        except ValueError:
            # Failed to parse TLS, this is probably due to a short read of a TLS
            # record.
            pass
        return request
    def on_response(self, response):
        if not self.ssl or self.signature_tampered:
            return response
        response = self.buffer + response
        self.buffer = ""
        # Tamper with the ServerKeyExchange message.
        try:
            index = 0
            while index < len(response):
                record, size = TlsRecord.from_stream(response[index:])
                version = record.version
                for i, message in enumerate(record.messages):
                    if (isinstance(message, HandshakeMessage)
                        and (message.type ==
                            HandshakeMessage.TYPE.SERVER_KEY_EXCHANGE)):
                        tampered_record_bytes = (
                            self._tamper_with_server_key_exchange(record, i))
                        response = (response[:index] +
                            tampered_record_bytes +
                            response[index+size:])
                        self.signature_tampered = True
                        return response

                index += size

        except ValueError:
            # Failed to parse TLS, this is probably due to a short read of a TLS
            # record. Buffer the response to try and get more data.
            self.buffer = response
            # But don't buffer too much, give up after 16k.
            if len(self.buffer) > 2**14:
                response = self.buffer
                self.buffer = ""
                return self.buffer
            return ""
        return response