示例#1
0
    def _send_message(self, message):
        b = message.render()
        if len(b) == 0:
            return  # Nothing to do

        content_type = message.content_type
        log.debug('Sending {0} record'.format(
            TLS_CONTENT_TYPE.get(content_type, content_type)))
        if isinstance(message, Handshake):
            log.debug('... with sub type {0}'.format(
                TLS_HANDSHAKE_TYPE.get(message.handshake_type,
                                       message.handshake_type)))

        # Add record header
        r = RecordHeader3()
        r.content_type = content_type
        r.version = self.client_version
        r.size = len(b)
        s = r.render() + b

        while True:
            sent = self.remote.send(s)
            if sent == len(s):
                return
            else:
                s = s[sent:]
                yield 1
示例#2
0
文件: pki.py 项目: tehmaze/tlsspy
    def verify(self, certificate):
        '''
        Verify the signature of ``certificate`` using the public key listed for
        this certificate.

        >>> issuer = Certificate(...)
        >>> victim = Certificate(...)
        >>> issuer.verify(victim)
        True
        '''
        # Don't bother verifying if we're not a CA certificate
        if not self.is_ca:
            log.debug('Attempted to verify from non-CA certificate')
            return False

        # Make sure this certificate is suitable for signing
        extensions = dict((k, v.to_python())
                          for k, v in self.get_extensions().iteritems())
        if not 'keyCertSign' in extensions.get('keyUsage', []):
            log.debug(
                'Attempted to verify from certificate not suitable for signing'
            )
            return False

        return self.get_public_key().verify(
            certificate.get_signature(),
            certificate.get_hash(),
            certificate.get_signature_algorithm(),
        )
示例#3
0
文件: pki.py 项目: shuxin/tlsspy
    def get_hash(self):
        '''
        :return: cryptographic hash of the tbsCertificate sequence
        '''
        signature_algorithm = self.get_signature_algorithm()
        algorithm = signature_algorithm.replace('WithRSAEncryption', '')
        log.debug('Generating hashed value for {0}'.format(algorithm, ))

        data = der_encoder.encode(self.tbsCertificate)
        if algorithm == 'md2':
            return md2.MD2(data).digest()
        elif algorithm == 'md5':
            return hashlib.md5(data).digest()
        elif algorithm == 'ripemd160':
            return ripemd160.RIPEMD160(data).digest()
        elif algorithm == 'sha1':
            return hashlib.sha1(data).digest()
        elif algorithm == 'sha224':
            return hashlib.sha224(data).digest()
        elif algorithm == 'sha256':
            return hashlib.sha256(data).digest()
        elif algorithm == 'sha384':
            return hashlib.sha384(data).digest()
        elif algorithm == 'sha512':
            return hashlib.sha512(data).digest()
        else:
            log.error('Unsupported signature algorithm: {0}'.format(
                signature_algorithm, ))
            return None
示例#4
0
    def _check_features(self, address, support):
        log.debug('Testing features')
        secure = self._connect(address)
        all_ciphers = [getattr(CipherSuite, suite)
                       for suite in self.collected['ciphers']]

        try:
            for result in secure.handshake(
                    server_name=address[0],
                    cipher_suites=all_ciphers,
                    compression_methods=[0, 1],
                    heartbeat=True,
                    supports_npn=True,
                    status_request=CertificateStatusType.ocsp,
                ):
                pass

            #import sys; sys.exit()

            # Test features from ServerHello report
            hello = secure.server_hello
            support['compression'] = hello.compression_method != 0
            support['heartbeat'] = hello.heartbeat
            support['next_protos'] = hello.next_protos
            support['secure_renegotiation'] = hello.secure_renegotiation
            support['session'] = bool(hello.session_id)
            if support['session']:
                support['session_id'] = str(hello.session_id).encode('hex')
            else:
                support['session_id'] = None
            support['session_ticket'] = bool(hello.session_ticket)

            secure.close()
        except socket.error as error:
            raise Probe.Skip('Network error: {0}'.format(error))
示例#5
0
文件: pki.py 项目: tehmaze/tlsspy
    def get_hash(self):
        '''
        :return: cryptographic hash of the tbsCertificate sequence
        '''
        signature_algorithm = self.get_signature_algorithm()
        algorithm = signature_algorithm.replace('WithRSAEncryption', '')
        log.debug('Generating hashed value for {0}'.format(
            algorithm,
        ))

        data = der_encoder.encode(self.tbsCertificate)
        if algorithm == 'md2':
            return md2.MD2(data).digest()
        elif algorithm == 'md5':
            return hashlib.md5(data).digest()
        elif algorithm == 'ripemd160':
            return ripemd160.RIPEMD160(data).digest()
        elif algorithm == 'sha1':
            return hashlib.sha1(data).digest()
        elif algorithm == 'sha224':
            return hashlib.sha224(data).digest()
        elif algorithm == 'sha256':
            return hashlib.sha256(data).digest()
        elif algorithm == 'sha384':
            return hashlib.sha384(data).digest()
        elif algorithm == 'sha512':
            return hashlib.sha512(data).digest()
        else:
            log.error('Unsupported signature algorithm: {0}'.format(
                signature_algorithm,
            ))
            return None
示例#6
0
文件: util.py 项目: tehmaze/tlsspy
    def _work(self, jobs, results):
        while True:
            job = jobs.get()

            if isinstance(job, ThreadPool.Done):
                log.debug('[{0}] done'.format(
                    threading.currentThread().name,
                ))
                # Bye!
                results.put(ThreadPool.Done())
                jobs.task_done()
                break

            func = job[0]
            args = job[1]
            try:
                result = func(*args)
            except Exception as error:
                log.error('Uncaught exception in thread worker: {0}'.format(
                    error,
                ))
            else:
                results.put(result)
            finally:
                jobs.task_done()
示例#7
0
 def open(self, directory, encoding='ascii'):
     path = os.path.join(directory, self.filename)
     log.debug('opening report {0}'.format(path))
     if path and self.filename != '-':
         return codecs.open(path, 'wb', encoding)
     else:
         return sys.stdout
示例#8
0
    def _send_message(self, message):
        b = message.render()
        if len(b) == 0:
            return  # Nothing to do

        content_type = message.content_type
        log.debug('Sending {0} record'.format(
            TLS_CONTENT_TYPE.get(content_type, content_type)
        ))
        if isinstance(message, Handshake):
            log.debug('... with sub type {0}'.format(
                TLS_HANDSHAKE_TYPE.get(message.handshake_type,
                                       message.handshake_type)
            ))

        # Add record header
        r = RecordHeader3()
        r.content_type = content_type
        r.version = self.client_version
        r.size = len(b)
        s = r.render() + b

        while True:
            sent = self.remote.send(s)
            if sent == len(s):
                return
            else:
                s = s[sent:]
                yield 1
示例#9
0
文件: pki.py 项目: shuxin/tlsspy
    def __init__(self, sequence):
        super(PublicKey, self).__init__(sequence)

        algorithm = self.sequence.getComponentByName(
            'algorithm').getComponentByName('algorithm')
        log.debug('Parsing {0} ({1}) public key'.format(
            friendly_oid(algorithm),
            str(algorithm),
        ))
        self.algorithm = x509.ID_KA_MAP.get(algorithm)

        if self.algorithm is None:
            raise TypeError('Unable to handle {0} keys'.format(str(algorithm)))

        key_bits = self.sequence.getComponentByName('subjectPublicKey')
        key_parm = self.sequence.getComponentByName(
            'algorithm').getComponentByName('parameters')
        key_type = self.get_type()

        if key_type == 'DSA':
            self.key, _ = self._get_DSA_public_key(key_bits, key_parm)

        elif key_type == 'RSA':
            self.key, _ = self._get_RSA_public_key(key_bits)

        elif key_type == 'EC':
            self.key, _ = self._get_EC_public_key(key_bits, key_parm)
示例#10
0
文件: pki.py 项目: tlsspy/tlsspy
    def __init__(self, sequence):
        super(PublicKey, self).__init__(sequence)

        algorithm = self.sequence.getComponentByName('algorithm').getComponentByName('algorithm')
        log.debug('Parsing {0} ({1}) public key'.format(
            friendly_oid(algorithm),
            str(algorithm),
        ))
        self.algorithm = x509.ID_KA_MAP.get(algorithm)

        if self.algorithm is None:
            raise TypeError('Unable to handle {0} keys'.format(str(algorithm)))

        key_bits = self.sequence.getComponentByName('subjectPublicKey')
        key_parm = self.sequence.getComponentByName('algorithm').getComponentByName('parameters')
        key_type = self.get_type()

        if key_type == 'DSA':
            self.key, _ = self._get_DSA_public_key(key_bits, key_parm)

        elif key_type == 'RSA':
            self.key, _ = self._get_RSA_public_key(key_bits)

        elif key_type == 'EC':
            self.key, _ = self._get_EC_public_key(key_bits, key_parm)
    def _handshake(self, secure, cipher_suites):
        log.debug('Selected {0} out of {1} ciphers'.format(
            len(cipher_suites),
            len(CipherSuite.all),
        ))

        for result in secure.handshake(cipher_suites=cipher_suites, ):
            pass
示例#12
0
文件: base.py 项目: tehmaze/tlsspy
 def open(self, encoding='ascii'):
     log.debug('opening report {0}'.format(
         self.filename,
     ))
     if self.filename and self.filename != '-':
         return codecs.open(self.filename, 'wb', encoding)
     else:
         return sys.stdout
示例#13
0
文件: ec.py 项目: shuxin/tlsspy
    def __init__(self, named_curve, key_data):
        log.debug('Parsing {0} ({1}) named curve'.format(
            friendly_oid(named_curve),
            named_curve,
        ))

        self.name = friendly_oid(named_curve)
        self.group = parse_named_curve(named_curve)
        self.point = parse_binary_point(key_data, self.group['point_size'])
    def _handshake(self, secure, cipher_suites):
        log.debug('Selected {0} out of {1} ciphers'.format(
            len(cipher_suites),
            len(CipherSuite.all),
        ))

        for result in secure.handshake(
                cipher_suites=cipher_suites,
            ):
            pass
示例#15
0
    def _recv_server_hello_resume(self, clientHello):
        '''
        Client                                                Server
        ClientHello
        (SessionTicket extension)      -------->
                                                         ServerHello
                                     (empty SessionTicket extension)
                                                    NewSessionTicket
                                                  [ChangeCipherSpec]
                                      <--------             Finished
        [ChangeCipherSpec]
        Finished                      -------->
        Application Data              <------->     Application Data
        '''
        for result in self._recv_message(
                ContentType.handshake,
                HandshakeType.server_hello
            ):
            if result in (0, 1):
                yield result
            else:
                break
        self.server_hello = result
        self.version = self.server_hello.server_version

        # Get ChangeCipherSpec
        for result in self._recv_message(
                (
                    ContentType.change_cipher_spec,
                    ContentType.handshake,
                ),
                (
                    HandshakeType.certificate,
                ),
                self.server_hello.certificate_type
            ):
            if result in (0, 1):
                yield result
            else:
                break

        if isinstance(result, ChangeCipherSpec):
            self.change_cipher_spec = result
        else:
            log.debug('Did not receive a change_cipher_spec message')
            self.change_cipher_spec = None
    def probe(self, address, certificates):
        '''
        Retrieves the X.509 certificate from the remote host, being as
        permissive as we can in terms of TLS protocol support, selected cipher
        suites and other protocol violations that may occur. Fetched
        certificates will be added to the ``certificates`` set.

        Provides the following keys:

        * ``analysis.features``

        Probes that depend on this probe:

        * 105_analyze_certificate_
        * 110_analyze_public_key_

        .. _105_analyze_certificate: probe_105_analyze_certificate.html
        .. _110_analyze_public_key:  probe_110_analyze_public_key.html
        '''

        if not address:
            # Nothing to do
            raise Probe.Skip('Offline; no address supplied')
        else:
            log.info('Fetching certificate from %s:%d' % address)

        features = {}
        features['long_client_handshake'] = True

        try:
            secure = self._connect(address)
            cipher = CipherSuite.all
            try:
                self._handshake(secure, CipherSuite.all)
            except Exception as error:
                log.debug('Long client handshake not supported: {0}'.format(
                    error
                ))
                features['long_client_handshake'] = False
                self._handshake(secure, CipherSuite.basic)

            for certificate in secure.get_certificate_chain():
                certificates.add(certificate)

        except socket.error, e:
            raise Probe.Skip('Network error: {0}'.format(e))
示例#17
0
def load_probe(filename):
    global PROBES

    name = '.'.join([
        'tlsspy',
        'probe',
        os.path.basename(os.path.splitext(filename)[0]),
    ])

    log.debug('Loading {0}'.format(name))
    try:
        module = imp.load_module(name, file(filename), filename,
                                 ('.py', 'U', imp.PY_SOURCE))
        PROBES[name] = getattr(module, 'PROBES', [])
    except Exception, e:
        log.warning('Loading {0} failed "{1}"'.format(name, e))
        raise
    def probe(self, address, certificates):
        '''
        Retrieves the X.509 certificate from the remote host, being as
        permissive as we can in terms of TLS protocol support, selected cipher
        suites and other protocol violations that may occur. Fetched
        certificates will be added to the ``certificates`` set.

        Provides the following keys:

        * ``analysis.features``

        Probes that depend on this probe:

        * 105_analyze_certificate_
        * 110_analyze_public_key_

        .. _105_analyze_certificate: probe_105_analyze_certificate.html
        .. _110_analyze_public_key:  probe_110_analyze_public_key.html
        '''

        if not address:
            # Nothing to do
            raise Probe.Skip('Offline; no address supplied')
        else:
            log.info('Fetching certificate from %s:%d' % address)

        features = {}
        features['long_client_handshake'] = True

        try:
            secure = self._connect(address)
            cipher = CipherSuite.all
            try:
                self._handshake(secure, CipherSuite.all)
            except Exception as error:
                log.debug(
                    'Long client handshake not supported: {0}'.format(error))
                features['long_client_handshake'] = False
                self._handshake(secure, CipherSuite.basic)

            for certificate in secure.get_certificate_chain():
                certificates.add(certificate)

        except socket.error, e:
            raise Probe.Skip('Network error: {0}'.format(e))
示例#19
0
文件: analyze.py 项目: tehmaze/tlsspy
    def analyze(self, address, certificates):
        if not isinstance(certificates, OrderedSet):
            certificates = OrderedSet(certificates)

        info = {'tests': [], 'tests_skipped': []}
        for Probe in self.probes:
            log.debug('Running {0}'.format(Probe.__module__))
            try:
                probe = Probe(info)
                probe.probe(address, certificates)
                info['tests'].append(Probe.__module__)
            except Probe.Skip, r:
                log.warning('Skip {0}: {1}'.format(Probe.__module__, r))
                info['tests_skipped'].append(Probe.__module__)
            except Exception as error:
                log.error('Uncaught exception: {0}'.format(error))
                for line in traceback.format_exc().splitlines():
                    log.error(line)
示例#20
0
文件: pki.py 项目: shuxin/tlsspy
    def __init__(self, sequence):
        self.sequence = sequence

        self.name = friendly_oid(self.sequence.getComponentByName('extnID'))
        self.critical = bool(
            self.sequence.getComponentByName('critical')._value)

        log.debug('Parsing extension {0}'.format(self.name))
        self.encoded = self.sequence.getComponentByName('extnValue')._value
        if self.name in self._decoders:
            self.decoded = der_decoder.decode(
                self.encoded, asn1Spec=self._decoders[self.name])[0]

        else:
            warnings.warn('Not able to decode extension {0}'.format(self.name))
            self.decoded = None

        self.parsed = self.to_python()
示例#21
0
    def _probe_named_curves(self, address, cipher_suites, elliptic_curves,
                            support, status):

        while elliptic_curves:
            try:
                curve = self._probe_named_curve(address, cipher_suites,
                                                elliptic_curves)
                if curve is None:
                    break
                else:
                    log.debug('Elliptic Named Curve {0} supported'.format(
                        TLS_EC_CURVE_NAME.get(curve), ))
                    name = TLS_EC_CURVE_NAME.get(curve)
                    support['curve_name'].append(name)
                    status.append({name: self._get_named_curve_info(curve)})
                    elliptic_curves.remove(curve)
            except Exception as error:
                log.debug('Error Probing Named Curve: {0}'.format(error, ))
                break
示例#22
0
文件: pki.py 项目: tehmaze/tlsspy
    def __init__(self, sequence):
        self.sequence = sequence

        self.name = friendly_oid(self.sequence['extnID'])
        self.critical = bool(self.sequence['critical']._value)

        log.debug('Parsing extension {0}'.format(self.name))
        if self.name in self._decoders:
            self.encoded = self.sequence.getComponentByName('extnValue')._value
            self.decoded = der_decoder.decode(
                self.encoded,
                asn1Spec=self._decoders[self.name]
            )[0]

        else:
            warnings.warn('Not able to decode extension {0}'.format(self.name))
            self.decoded = None

        self.parsed = self.to_python()
示例#23
0
文件: loader.py 项目: tehmaze/tlsspy
def load_probe(filename):
    global PROBES

    name = '.'.join([
        'tlsspy', 'probe',
        os.path.basename(os.path.splitext(filename)[0]),
    ])

    log.debug('Loading {0}'.format(name))
    try:
        module = imp.load_module(
            name,
            file(filename),
            filename,
            ('.py', 'U', imp.PY_SOURCE)
        )
        PROBES[name] = getattr(module, 'PROBES', [])
    except Exception, e:
        log.warning('Loading {0} failed "{1}"'.format(name, e))
        raise
示例#24
0
文件: pki.py 项目: shuxin/tlsspy
    def verify(self, certificate):
        '''
        Verify the signature of ``certificate`` using the public key listed for
        this certificate.

        >>> issuer = Certificate(...)
        >>> victim = Certificate(...)
        >>> issuer.verify(victim)
        True
        '''
        log.debug('{0} verify {1}'.format(
            self,
            certificate,
        ))
        # Don't bother verifying if we're not a CA certificate
        if not self.is_ca:
            log.debug('Attempted to verify from non-CA certificate')
            return False

        # Make sure this certificate is suitable for signing
        extensions = dict(
            (k, v.to_python()) for k, v in self.get_extensions().iteritems())
        if not 'keyCertSign' in extensions.get('keyUsage', []):
            log.debug(
                'Attempted to verify from certificate not suitable for signing'
            )
            return False

        return self.get_public_key().verify(
            certificate.get_signature(),
            certificate.get_hash(),
            certificate.get_signature_algorithm(),
        )
示例#25
0
 def _test_cipher(self, address, *cipher_suites):
     try:
         secure = self._connect(address)
         # Construct accepted ciphers, also include a pseudo cipher
         cipher_suites = list(cipher_suites)
         if not CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV in cipher_suites:
             cipher_suites.append(
                 CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
         # Consume generator
         for result in secure.handshake(
                 cipher_suites=cipher_suites,
                 server_name=address[0],
         ):
             pass
     except Exception as error:
         log.debug('Cipher failed: {0}'.format(error))
         return None
     else:
         log.debug('Cipher accepted: {0} chosen by server'.format(
             TLS_CIPHER_SUITE[secure.server_hello.cipher_suite], ))
         secure.close()
         return secure.server_hello.cipher_suite
示例#26
0
    def _recv_server_hello_resume(self, clientHello):
        '''
        Client                                                Server
        ClientHello
        (SessionTicket extension)      -------->
                                                         ServerHello
                                     (empty SessionTicket extension)
                                                    NewSessionTicket
                                                  [ChangeCipherSpec]
                                      <--------             Finished
        [ChangeCipherSpec]
        Finished                      -------->
        Application Data              <------->     Application Data
        '''
        for result in self._recv_message(ContentType.handshake,
                                         HandshakeType.server_hello):
            if result in (0, 1):
                yield result
            else:
                break
        self.server_hello = result
        self.version = self.server_hello.server_version

        # Get ChangeCipherSpec
        for result in self._recv_message((
                ContentType.change_cipher_spec,
                ContentType.handshake,
        ), (HandshakeType.certificate, ), self.server_hello.certificate_type):
            if result in (0, 1):
                yield result
            else:
                break

        if isinstance(result, ChangeCipherSpec):
            self.change_cipher_spec = result
        else:
            log.debug('Did not receive a change_cipher_spec message')
            self.change_cipher_spec = None
示例#27
0
文件: ec.py 项目: shuxin/tlsspy
def parse_binary_point(blob, point_size):
    data = bytearray(blob)
    point_format = data[0]
    point_data = data[1:]
    if point_format in POINT_NULL:
        if len(point_data) == 0:
            return dict(x=1, y=1, z=0)
        else:
            raise ValueError()

    elif point_format in POINT_COMPRESSED:
        log.debug('Parsing compressed point of {0} bytes with size {1}'.format(
            len(point_data),
            point_size,
        ))
        rest = point_data[point_size:]
        assert not rest, '{0} bytes remain in compressed binary point'.format(
            len(rest))
        x = bytes_to_long(point_data[:point_size])
        y = x * ((point_format - 2) * -1)
        return dict(x=x, y=y, z=0)

    elif point_format in POINT_UNCOMPRESSED:
        log.debug(
            'Parsing uncompressed point of {0} bytes with size {1}'.format(
                len(point_data),
                point_size,
            ))
        rest = point_data[point_size + point_size:]
        assert not rest, '{0} bytes remain in binary point'.format(len(rest), )
        return dict(
            x=bytes_to_long(point_data[:point_size]),
            y=bytes_to_long(point_data[point_size:]),
            z=1,
        )

    else:
        raise ValueError('Unsupported point format {0:02x}'.format(data[0], ))
示例#28
0
    def _check_feature_session_resumption(self, address, support):
        log.debug('Testing session resumption')
        session_id = bytearray(support['session_id'].decode('hex'))
        secure = self._connect(address)

        try:
            cipher = CipherSuite.filter(key_exchange=('RSA', 'DH'))
            for result in secure.resume(
                    server_name=address[0],
                    cipher_suites=cipher,
                    session_id=session_id,
                ):
                pass

            if secure.server_hello.session_id == session_id:
                support['session_resumed'] = True
            else:
                support['session_resumed'] = False

            secure.close()

        except socket.error as error:
            raise Probe.Skip('Network error: {0}'.format(error))
示例#29
0
    def _probe_named_curves(self, address, cipher_suites, elliptic_curves,
                            support, status):

        while elliptic_curves:
            try:
                curve = self._probe_named_curve(address, cipher_suites, elliptic_curves)
                if curve is None:
                    break
                else:
                    log.debug('Elliptic Named Curve {0} supported'.format(
                        TLS_EC_CURVE_NAME.get(curve),
                    ))
                    name = TLS_EC_CURVE_NAME.get(curve)
                    support['curve_name'].append(name)
                    status.append({
                        name: self._get_named_curve_info(curve)
                    })
                    elliptic_curves.remove(curve)
            except Exception as error:
                log.debug('Error Probing Named Curve: {0}'.format(
                    error,
                ))
                break
示例#30
0
    def _check_feature_session_resumption(self, address, support):
        log.debug('Testing session resumption')
        session_id = bytearray(support['session_id'].decode('hex'))
        secure = self._connect(address)

        try:
            cipher = CipherSuite.filter(key_exchange=('RSA', 'DH'))
            for result in secure.resume(
                    server_name=address[0],
                    cipher_suites=cipher,
                    session_id=session_id,
            ):
                pass

            if secure.server_hello.session_id == session_id:
                support['session_resumed'] = True
            else:
                support['session_resumed'] = False

            secure.close()

        except socket.error as error:
            raise Probe.Skip('Network error: {0}'.format(error))
示例#31
0
文件: util.py 项目: shuxin/tlsspy
    def _work(self, jobs, results):
        while True:
            job = jobs.get()

            if isinstance(job, ThreadPool.Done):
                log.debug('[{0}] done'.format(
                    threading.currentThread().name, ))
                # Bye!
                results.put(ThreadPool.Done())
                jobs.task_done()
                break

            func = job[0]
            args = job[1]
            try:
                result = func(*args)
            except Exception as error:
                log.error('Uncaught exception in thread worker: {0}'.format(
                    error, ))
            else:
                results.put(result)
            finally:
                jobs.task_done()
示例#32
0
    def verify(self, signature, data, signature_algorithm):
        exponent = self.get_exponent()
        modulus = self.get_modulus_long()
        modulus_size = num_bytes(modulus)
        signature_size = len(signature)
        if modulus_size != signature_size:
            log.debug(
                'Signature length {0} does not match our key size {1}'.format(
                    signature_size,
                    modulus_size,
                ))
            return False

        prefix = self._add_pkcs1_prefix(data, signature_algorithm)
        padded = self._add_pkcs1_padding(prefix, 1)
        c = bytes_to_long(bytearray(signature))
        if c >= modulus:
            log.debug('Signature data exceeds modulus')
            return False

        m = pow_mod(c, exponent, modulus)
        check = long_to_bytes(m, modulus_size)
        return check == padded
示例#33
0
 def _test_cipher(self, address, *cipher_suites):
     try:
         secure = self._connect(address)
         # Construct accepted ciphers, also include a pseudo cipher
         cipher_suites = list(cipher_suites)
         if not CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV in cipher_suites:
             cipher_suites.append(
                 CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
             )
         # Consume generator
         for result in secure.handshake(
                 cipher_suites=cipher_suites,
                 server_name=address[0],
             ):
             pass
     except Exception as error:
         log.debug('Cipher failed: {0}'.format(error))
         return None
     else:
         log.debug('Cipher accepted: {0} chosen by server'.format(
             TLS_CIPHER_SUITE[secure.server_hello.cipher_suite],
         ))
         secure.close()
         return secure.server_hello.cipher_suite
示例#34
0
    def _check_features(self, address, support):
        log.debug('Testing features')
        secure = self._connect(address)
        all_ciphers = [
            getattr(CipherSuite, suite) for suite in self.collected['ciphers']
        ]

        try:
            for result in secure.handshake(
                    server_name=address[0],
                    cipher_suites=all_ciphers,
                    compression_methods=[0, 1],
                    heartbeat=True,
                    supports_npn=True,
                    status_request=CertificateStatusType.ocsp,
            ):
                pass

            #import sys; sys.exit()

            # Test features from ServerHello report
            hello = secure.server_hello
            support['compression'] = hello.compression_method != 0
            support['heartbeat'] = hello.heartbeat
            support['next_protos'] = hello.next_protos
            support['secure_renegotiation'] = hello.secure_renegotiation
            support['session'] = bool(hello.session_id)
            if support['session']:
                support['session_id'] = str(hello.session_id).encode('hex')
            else:
                support['session_id'] = None
            support['session_ticket'] = bool(hello.session_ticket)

            secure.close()
        except socket.error as error:
            raise Probe.Skip('Network error: {0}'.format(error))
示例#35
0
文件: rsa.py 项目: tlsspy/tlsspy
    def verify(self, signature, data, signature_algorithm):
        exponent = self.get_exponent()
        modulus = self.get_modulus_long()
        modulus_size = num_bytes(modulus)
        signature_size = len(signature)
        if modulus_size != signature_size:
            log.debug(
                'Signature length {0} does not match our key size {1}'.format(
                    signature_size,
                    modulus_size,
                )
            )
            return False

        prefix = self._add_pkcs1_prefix(data, signature_algorithm)
        padded = self._add_pkcs1_padding(prefix, 1)
        c = bytes_to_long(bytearray(signature))
        if c >= modulus:
            log.debug('Signature data exceeds modulus')
            return False

        m = pow_mod(c, exponent, modulus)
        check = long_to_bytes(m, modulus_size)
        return check == padded
示例#36
0
    def _check_elliptic_curves(self, address, support, status):
        log.debug('Testing Elliptic Curves')
        ciphers = [getattr(CipherSuite, suite)
                   for suite in self.collected['ciphers']
                   if '_EC' in suite]

        if ciphers:
            log.debug('Discovered {0} usable cipher suites using EC'.format(
                len(ciphers),
            ))
            elliptic_curves = TLS_EC_CURVE_NAME.keys()
            elliptic_curves.sort()
            ec_point_formats = TLS_EC_POINT_FORMAT.keys()
            ec_point_formats.sort()
        else:
            log.debug('Discovered no cipher suites using EC, skipping feature')
            raise Probe.Skip('no cipher suites supporting elliptic curves')

        try:
            secure = self._connect(address)
            for result in secure.handshake(
                    server_name=address[0],
                    cipher_suites=ciphers,
                    elliptic_curves=elliptic_curves,
                    ec_point_formats=ec_point_formats,
                ):
                pass

        except socket.error as error:
            raise Probe.Skip('Elliptic Curve Point Format handshake failed: '
                             '{0}'.format(error))

        # Map out the names of the EC point formats supported by the server
        ec_point_formats = secure.server_hello.ec_point_formats
        support['point_format'] = filter(None, map(
            TLS_EC_POINT_FORMAT.get,
            ec_point_formats
        ))

        support['curve_name'] = []
        support['curve_type'] = None
        if secure.server_key_exchange is not None:
            support['curve_type'] = TLS_EC_CURVE_TYPE.get(
                secure.server_key_exchange.ec_curve_type,
                'unsupported',
            )

            if secure.server_key_exchange.ec_curve_type == ECCurveType.named_curve:
                # Find out what named curves the server supports
                self._probe_named_curves(address, ciphers, elliptic_curves,
                                         support, status)
示例#37
0
    def _check_elliptic_curves(self, address, support, status):
        log.debug('Testing Elliptic Curves')
        ciphers = [
            getattr(CipherSuite, suite) for suite in self.collected['ciphers']
            if '_EC' in suite
        ]

        if ciphers:
            log.debug('Discovered {0} usable cipher suites using EC'.format(
                len(ciphers), ))
            elliptic_curves = TLS_EC_CURVE_NAME.keys()
            elliptic_curves.sort()
            ec_point_formats = TLS_EC_POINT_FORMAT.keys()
            ec_point_formats.sort()
        else:
            log.debug('Discovered no cipher suites using EC, skipping feature')
            raise Probe.Skip('no cipher suites supporting elliptic curves')

        try:
            secure = self._connect(address)
            for result in secure.handshake(
                    server_name=address[0],
                    cipher_suites=ciphers,
                    elliptic_curves=elliptic_curves,
                    ec_point_formats=ec_point_formats,
            ):
                pass

        except socket.error as error:
            raise Probe.Skip('Elliptic Curve Point Format handshake failed: '
                             '{0}'.format(error))

        # Map out the names of the EC point formats supported by the server
        ec_point_formats = secure.server_hello.ec_point_formats
        support['point_format'] = filter(
            None, map(TLS_EC_POINT_FORMAT.get, ec_point_formats))

        support['curve_name'] = []
        support['curve_type'] = None
        if secure.server_key_exchange is not None:
            support['curve_type'] = TLS_EC_CURVE_TYPE.get(
                secure.server_key_exchange.ec_curve_type,
                'unsupported',
            )

            if secure.server_key_exchange.ec_curve_type == ECCurveType.named_curve:
                # Find out what named curves the server supports
                self._probe_named_curves(address, ciphers, elliptic_curves,
                                         support, status)
    def probe(self, address, certificates):
        '''
        Analyze the public key for each certificate in the ``certificates`` set.

        Provides the following keys:

        * ``analysis.public_keys``

        Uses the following configuration keys:

        * ``analyze.public_key.key_sizes``, which is a dictionary, containing:

          * key type, which is a dictionary, containing:

            * ``bits``, minimal number of security bits in the key
            * ``docs``, reference to the minimal size documentation
        '''
        key_infos = []
        warnings = defaultdict(list)
        errors = defaultdict(list)

        for certificate in certificates:
            public_key = certificate.get_public_key()
            log.debug('Analyzing {0} bit {1} key'.format(
                public_key.get_bits(),
                public_key.get_type(),
            ))

            key_info = dict(status='good')
            key_bits = public_key.get_bits()
            key_type = public_key.get_type()
            key_conf = self.config.get('key_sizes', {}).get(key_type)
            key_name = '{0} {1} bits'.format(key_type, key_bits)
            if key_conf:
                if key_bits < key_conf['bits']:
                    key_info = dict(
                        status='error',
                        reason='{0} bits {1} key is less than {2}: {3}'.format(
                            key_bits,
                            key_type,
                            key_conf['bits'],
                            key_conf['docs'],
                        ))

                elif key_type == 'rsa':
                    modulus = public_key.get_modulus()
                    exponent = public_key.get_exponent()
                    if modulus < B1023:
                        key_info = dict(
                            status='error',
                            reason='Weak key',
                        )
                    elif exponent < 65537:
                        key_info = dict(
                            status='error',
                            reason='Weak exponent used 0x{:04x}'.format(
                                exponent, ))

            else:
                key_info = dict(
                    status='error',
                    reason='Unsupported public key algorithm',
                )

            key_infos.append({key_name: key_info})

        return self.merge(
            dict(
                analysis=dict(public_keys=key_infos),
                errors=errors,
                warnings=warnings,
            ))
    def probe(self, address, certificates):
        '''
        Analyze the public key for each certificate in the ``certificates`` set.

        Provides the following keys:

        * ``analysis.public_keys``

        Uses the following configuration keys:

        * ``analyze.public_key.key_sizes``, which is a dictionary, containing:

          * key type, which is a dictionary, containing:

            * ``bits``, minimal number of security bits in the key
            * ``docs``, reference to the minimal size documentation
        '''
        key_infos = []
        warnings  = defaultdict(list)
        errors    = defaultdict(list)

        for certificate in certificates:
            public_key = certificate.get_public_key()
            log.debug('Analyzing {0} bit {1} key'.format(
                public_key.get_bits(),
                public_key.get_type(),
            ))

            key_info = dict(status='good')
            key_bits = public_key.get_bits()
            key_type = public_key.get_type()
            key_conf = self.config.get('key_sizes', {}).get(key_type)
            key_name = '{0} {1} bits'.format(key_type, key_bits)
            if key_conf:
                if key_bits < key_conf['bits']:
                    key_info = dict(
                        status='error',
                        reason='{0} bits {1} key is less than {2}: {3}'.format(
                            key_bits,
                            key_type,
                            key_conf['bits'],
                            key_conf['docs'],
                        )
                    )

                elif key_type == 'rsa':
                    modulus = public_key.get_modulus()
                    exponent = public_key.get_exponent()
                    if modulus < B1023:
                        key_info = dict(
                            status='error',
                            reason='Weak key',
                        )
                    elif exponent < 65537:
                        key_info = dict(
                            status='error',
                            reason='Weak exponent used 0x{:04x}'.format(
                                exponent,
                            )
                        )

            else:
                key_info = dict(
                    status='error',
                    reason='Unsupported public key algorithm',
                )

            key_infos.append({key_name: key_info})

        return self.merge(dict(
            analysis=dict(public_keys=key_infos),
            errors=errors,
            warnings=warnings,
        ))
示例#40
0
    def probe(self, address, certificates):
        '''
        Analyze the cipher suites supported by the server. Also try to establish
        if the server has a preferred cipher order.

        Provides the following keys:

        * ``analysis.ciphers``
        * ``analysis.features``
        * ``ciphers``
        '''
        if address is None:
            raise Probe.Skip('offline; no address supplied')

        # Features
        features = {}
        features['forward_secrecy'] = False  # Detect later
        features['preferred_order'] = False  # Detect later

        # Enlist all ciphers, start with NULL cipher first
        all_cipher_suites = TLS_CIPHER_SUITE.keys()
        all_cipher_suites.sort()

        # Remove our pseudo-cipher, it's added later in the check
        all_cipher_suites.remove(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)

        # Test what cipher the server selects
        cipher_suite1 = self._test_cipher(address, *all_cipher_suites)
        if cipher_suite1 is None:
            log.error('No cipher suite selected by server?!')
            return

        # Now test what cipher the server selects next
        all_cipher_suites.remove(cipher_suite1)
        cipher_suite2 = self._test_cipher(address, *all_cipher_suites)
        if cipher_suite2 is None:
            log.error('No cipher suite selected by server?!')
            return

        # Now that we have two ciphers, offer them in reverse order. If the
        # server selects cipher1 again, the server has a preferred order.
        all_cipher_suites = [
            cipher_suite2,
            cipher_suite1,
        ]
        if self._test_cipher(address, *all_cipher_suites) == cipher_suite1:
            order = True
        else:
            order = False

        # Store feature
        features['preferred_order'] = order

        # Start bulk-testing ciphers
        all_cipher_suites = TLS_CIPHER_SUITE.keys()
        all_cipher_suites.sort()
        all_cipher_suites.remove(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
        all_cipher_suites.remove(cipher_suite1)
        all_cipher_suites.remove(cipher_suite2)
        our_cipher_suites = [
            cipher_suite1,
            cipher_suite2,
        ]

        if order:
            log.info('Serial scanning {0} suites in server order'.format(
                len(all_cipher_suites),
            ))
            while all_cipher_suites:
                try:
                    cipher_suite = self._test_cipher(address, *all_cipher_suites)
                except Exception as error:
                    log.debug('None of our suites are accepted, stopping')
                    cipher_suite = None

                if cipher_suite is None:
                    break
                else:
                    our_cipher_suites.append(cipher_suite)
                    all_cipher_suites.remove(cipher_suite)

        else:
            # If there is no server-preferred order, we can use parallel
            # (threaded) probing
            parallel = self.config.get('parallel', 0)
            if parallel:
                log.info('Parallel scanning {0} suites'.format(
                    len(all_cipher_suites),
                ))
                pool = ThreadPool()

                for cipher_suite in reversed(all_cipher_suites):
                    pool.add_job(self._test_cipher, (address, cipher_suite))

                pool.start(parallel)
                for cipher_suite in pool.get_results():
                    if cipher_suite is not None:
                        our_cipher_suites.append(cipher_suite)

                pool.join()

            else:
                log.info('Serial scanning {0} suites'.format(
                    len(all_cipher_suites),
                ))
                for cipher_suite in reversed(all_cipher_suites):
                    if self._test_cipher(address, cipher_suite):
                        our_cipher_suites.append(cipher_suite)

        # Post-processing
        log.debug('Discovered {0} usable cipher suites'.format(
            len(our_cipher_suites),
        ))
        cipher_names = []
        support = []
        for cipher in our_cipher_suites:
            name, info = get_cipher_info(cipher)
            cipher_names.append(name)

            if info['encryption'] is None:
                info.update(dict(
                    status='error',
                    reason='Cipher offers no encryption'
                ))

            elif info['authentication'] is None:
                info.update(dict(
                    status='error',
                    reason='Cipher offers no authentication'
                ))

            elif info['encryption'] in ('DES', 'DES40', 'IDEA'):
                info.update(dict(
                    status='error',
                    reason='Weak encryption',
                ))

            elif info['encryption_bits'] < 112:
                info.update(dict(
                    status='error',
                    reason='Cipher offers weak encryption, only {0} bits'.format(
                        info['encryption_bits'],
                    )
                ))

            elif info['encryption_bits'] < 128:
                info.update(dict(
                    status='warning',
                    reason='Cipher offers weak encryption, only {0} bits'.format(
                        info['encryption_bits'],
                    )
                ))

            elif info['protocol'] == 'SSL':
                info.update(dict(
                    status='error',
                    reason='Cipher uses weak SSL implementation',
                ))

            else:
                if info['key_exchange'] in ('DHE', 'ECDHE'):
                    features['forward_secrecy'] = True
                info['status'] = 'good'

            support.append({name: info})

        self.merge(dict(
            analysis=dict(
                ciphers=support,
                features=features,
            ),
            ciphers=cipher_names,
        ))
示例#41
0
    def _test_version(self, address, version):
        '''
        Returns the version supported by the server for client version
        ``version``.

        :arg address: address tuple
        :arg version: version tuple, consisting of (``major``, ``minor``)
                      version numbers
        '''
        log.debug('Testing TLS/SSL version 0x{0:02x}{1:02x}'.format(*version))
        chello = ClientHello()
        chello.client_version = version
        chello.random = get_random_bytes(32)
        chello.cipher_suites = TLS_CIPHER_SUITE.keys()  # Be very permissive
        packet = chello.render()

        remote = Remote(address)
        remote.connect()

        try:
            header = RecordHeader3()
            header.content_type = chello.content_type
            header.version = version
            header.size = len(packet)

            # Send request
            r = header.render() + packet
            remote.send_all(r)

            # Read response
            try:
                r = Reader(bytearray(remote.recv(1024)))
                content_type = r.get(1)
            except SyntaxError:
                raise ValueError('Expected record header')

            if content_type != ContentType.handshake:
                raise SyntaxError('Expected handshake, got {0} (0x{1:02x})'.format(
                    TLS_CONTENT_TYPE.get(content_type, content_type),
                    content_type,
                ))

            header_version = (r.get(1), r.get(1))
            # SSLv3/TLSv1.x
            if header_version >= (0x03, 0x00):
                header_size = r.get(2)
                b = r.get_fixed(header_size)
                if b[0] != HandshakeType.server_hello:
                    raise SyntaxError('Expected server hello, got {0} (0x{1:02x})'.format(
                        TLS_HANDSHAKE_TYPE.get(b[0], b[0]),
                        b[0]
                    ))
                server_hello = ServerHello().parse(Reader(b[1:]))
                return server_hello.server_version

            # SSLv2
            else:
                return header_version

        finally:
            remote.close()
    def check_trust(self, certificate):
        '''
        Check if the certificate provided in ``certificate`` is trusted by:

        1. checking if the certificate has a trust anchor in our trust store, if
           so, check the certificate validity with the trust store
        2. checking if the previous certificate was trusted and is a
           certificate authority, if so, check the certificate validity with the
           previously provided certificate in the chain
        3. checking if the certificate is self signed
        '''
        log.debug('Analyzing {0}'.format(certificate.get_subject_str()))

        subject_hash = certificate.get_subject_hash()
        issuer = certificate.get_issuer()
        issuer_hash = certificate.get_issuer_hash()
        issuer_name = issuer.get(
            'commonName',
            issuer.get('organizationName', issuer_hash)
        )
        trusted = (subject_hash in TRUST_STORE)

        if subject_hash not in self.chain_hash:
            self.chain.append(certificate)
            self.chain_hash.append(subject_hash)

        if self.trust and self.trust[-1]['status'] != 'good':
            yield dict(
                status='error',
                reason='Invalid chain',
            )
            return

        # Self-signed certificate
        if subject_hash == issuer_hash:
            if subject_hash in TRUST_STORE:
                # Certificate should be able to verify itself
                issuer = TRUST_STORE[subject_hash]
                status = issuer.verify(certificate)
                if status is True:
                    log.debug('Issuer "{0}" in trust store'.format(
                        issuer_name,
                    ))
                    yield dict(
                        status='good',
                        reason='In trust store',
                    )
                    return

                elif status is None:
                    yield dict(
                        status='unknown',
                        reason='Unable to verify (local issue)',
                    )

            # Untrusted self-signed certificate
            log.debug('Self-signed certificate in chain')
            yield dict(trust=dict(
                status='error',
                reason='Self-signed certificate in chain',
            ))
            return

        also_check = []
        if issuer_hash in TRUST_STORE:
            issuer = TRUST_STORE[issuer_hash]
            log.debug('Issuer "{0}" in trust store'.format(issuer_name))
            if issuer_hash not in self.chain_hash:
                also_check.append(issuer)

            if issuer.verify(certificate):
                yield dict(
                    status='good',
                    reason='In trust store',
                )
            else:
                yield dict(
                    status='error',
                    reason='Verification failed',
                )

        elif issuer_hash in self.chain_hash:
            log.debug('Issuer {0} in trust chain'.format(issuer_name))
            issuer = self.chain[self.chain_hash.index(issuer_hash)]
            if issuer.verify(certificate):
                yield dict(
                    status='good',
                    reason='In trust chain',
                )
            else:
                yield dict(
                    status='error',
                    reason='Verification failed',
                )

        else:
            yield dict(
                status='error',
                reason='Issuer {0} unknown'.format(issuer_name),
            )

        for check in also_check:
            for trust in self.check_trust(check):
                yield trust
示例#43
0
    def _recv_message(self,
                      expected_type,
                      secondary_type=None,
                      constructor_type=None):

        if not isinstance(expected_type, tuple):
            expected_type = (expected_type, )

        while True:
            for result in self._recv_next_record():
                if result in (0, 1):
                    yield result
            record_header, r = result
            content_type = record_header.content_type

            log.debug('Received {0} record'.format(
                TLS_CONTENT_TYPE.get(content_type, content_type)))
            if content_type == ContentType.application_data:
                if r.pos == len(r):
                    continue

            if content_type not in expected_type:
                if content_type == ContentType.alert:
                    Alert().parse(r).throw()

                raise ValueError(
                    'Unexpected record type {0}, expected {1}'.format(
                        TLS_CONTENT_TYPE.get(content_type, content_type),
                        map(TLS_CONTENT_TYPE.get, expected_type)))

            # Parse based on content_type
            if content_type == ContentType.alert:
                yield Alert().parse(r)

            elif content_type == ContentType.change_cipher_spec:
                yield ChangeCipherSpec().parse(r)

            elif content_type == ContentType.handshake:
                if not isinstance(secondary_type, tuple):
                    secondary_type = (secondary_type, )

                if record_header.v2:
                    sub_type = r.get(1)
                    if sub_type != HandshakeType.client_hello:
                        raise TypeError('Expected client hello')
                    if HandshakeType.client_hello not in secondary_type:
                        raise TypeError('Unexpected message')
                    sub_type = HandshakeType.client_hello

                else:
                    sub_type = r.get(1)
                    if sub_type not in secondary_type:
                        raise TypeError(
                            'Unexpected message {0} ({1}), '
                            'expected {2}/{3}'.format(
                                sub_type,
                                TLS_HANDSHAKE_TYPE.get(sub_type, 'unknown'),
                                map(TLS_HANDSHAKE_TYPE.get, expected_type),
                                map(TLS_HANDSHAKE_TYPE.get, secondary_type)))

                log.debug('... with sub type {0}'.format(
                    TLS_HANDSHAKE_TYPE.get(sub_type, sub_type)))

                if sub_type == HandshakeType.client_hello:
                    yield ClientHello(record_header.v2).parse(r)

                elif sub_type == HandshakeType.server_hello:
                    yield ServerHello(record_header.v2).parse(r)

                elif sub_type == HandshakeType.certificate:
                    yield Certificate(constructor_type).parse(r)

                elif sub_type == HandshakeType.certificate_status:
                    yield CertificateStatus().parse(r)

                elif sub_type == HandshakeType.server_key_exchange:
                    yield ServerKeyExchange(constructor_type).parse(r)

                elif sub_type == HandshakeType.server_hello_done:
                    yield ServerHelloDone().parse(r)

                else:
                    raise AssertionError(
                        TLS_HANDSHAKE_TYPE.get(sub_type, sub_type))
示例#44
0
    def get_host_name(self, host, port=0):
        # Try IPv6 resolving
        try:
            socket.inet_pton(socket.AF_INET6, host)
            if have_dns:
                try:
                    name = reversename.from_address(host)
                    name = str(resolver.query(name, 'PTR')[0]).rstrip('.')
                    log.debug('{0} resolved to {1}'.format(host, name))
                    return name
                except Exception as error:
                    log.debug('{0} failed to resolve: {1}'.format(host, error))
                    return host
            else:
                return host
        except socket.error:
            pass

        # Try IPv4 resolving
        try:
            socket.inet_pton(socket.AF_INET, host)
            if have_dns:
                try:
                    name = reversename.from_address(host)
                    name = str(resolver.query(name, 'PTR')[0]).rstrip('.')
                    log.debug('{0} resolved to {1}'.format(host, name))
                    return name
                except Exception as error:
                    log.debug('{0} failed to resolve: {1}'.format(host, error))
                    return host
            else:
                name = socket.gethostbyaddr(host)[0]
                if name:
                    log.debug('{0} resolved to {1}'.format(host, name))
                    return name
        except socket.error:
            pass

        # Give up
        log.debug('{0} failed to resolve: not an IP'.format(host))
        return host
示例#45
0
    def probe(self, address, certificates):
        '''
        Tests for the Heartbleed TLS attack which targets protocols that have
        the heartbeat extension enabled and do improper boundary checks, such
        as found in OpenSSL versions between 1.0.1 - 1.0.1f.

        Provides the following keys:

        * ``weakness.heartbleed``
        '''
        if address is None:
            raise Probe.Skip('offline; no address supplied')

        weakness = {}
        weakness['status'] = 'good'
        weakness['exists'] = False
        weakness['reason'] = 'Heartbeat not enabled'

        try:
            remote = socket.create_connection(address)
        except socket.error as error:
            raise Probe.Skip('Network error: {0}'.format(error))

        if remote:
            remote.send(TLS_HELLO)
            while True:
                try:
                    typ, version, payload = self._get_msg(remote)
                except ValueError as error:
                    log.debug('Oops: {0}'.format(error))
                    remote = None
                    break
                else:
                    if typ == 22 and ord(payload[0]) == 0x0e:
                        break

        if remote:
            remote.send(TLS_HEARTBEAT)
            while True:
                remote.send(TLS_HEARTBEAT)
                try:
                    typ, version, payload = self._get_msg(remote)
                except ValueError as error:
                    log.debug('Oops: {0}'.format(error))
                    break
                else:
                    if typ == 24:
                        log.debug('Received heartbeat response')
                        if len(payload) > 3:
                            weakness['status'] = 'error'
                            weakness['exists'] = True
                            weakness['reason'] = 'Server returned more data ' +\
                                                 'than it should have'
                        else:
                            weakness['reason'] = 'Server-side fixed'

                        break

                    elif typ == 21:
                        # Alert
                        break

            remote.close()

        self.merge(dict(weakness=dict(heartbleed=weakness)))
示例#46
0
    def probe(self, address, certificates):
        '''
        Analyze the available protocol versions and protocol intolerance. The
        TLS protocol negotiates what protocol version to use like so:

        1. The client initiates a TLS handshake, sending a ClientHello packet,
           including the highest protocol version it supports.
        2. The server responds with a ServerHello packet, indicating the higest
           protocol version it supports, but no higher than the version
           requested by the client. If the Server not not willing to support
           older versions, it will respond with an Alert packet in stead.

        So if the client were to request a hypothetical TLSv2.0 (``0x4000``)
        protocol version, and the server supports up to TLSv1.2 (``0x0303``),
        it should reply with version ``0x0303``. Unfortunately a lot of TLS/SSL
        stacks are broken and respond with an incorrect version, called protocol
        version intolerance. This may lead to interoperability issues for
        clients that support newer versions of the protocol. If the server
        claims to support newer versions, but it doesn't know how to properly
        respond to the ClientHello, the handshake setup may fail and the client
        might not be able to connect.

        Provides the following keys:

        * ``analysis.protocol_intolerance``
        * ``analysis.protocols``
        '''
        if address is None:
            raise Probe.Skip('offline; no address supplied')

        protocols = []
        for version, statuses in PROTOCOLS.iteritems():
            name = TLS_VERSION[version]
            try:
                self._test_version(address, version)
                status = statuses[STATUS_OK]
                status['available'] = True
                protocols.append({name: status})
            except Exception as error:
                status = statuses[STATUS_ERROR]
                status['available'] = False
                status['reason'] = status.get(
                    'reason',
                    'Not available: {0}'.format(error)
                )
                protocols.append({name: status})

        protocol_intolerance = []
        for version, max_server_version in TLS_VERSION_TOLERANCE.iteritems():
            name = '{0} {1}.{2}'.format(
                'TLS' if version[0] > 2 else 'SSL',
                version[0] - 2,
                version[1],
            )
            try:
                server_version = self._test_version(address, version)
                server_name = '{0} {1}.{2}'.format(
                    'TLS' if server_version[0] > 2 else 'SSL',
                    server_version[0] - 2,
                    server_version[1],
                )
                if server_version > max_server_version:
                    log.debug('Intolerant for version {0} (got {1} !?)'.format(
                        name,
                        server_name,
                    ))
                    protocol_intolerance.append(name)
                else:
                    log.debug('Proper response for version {0} (got {1})'.format(
                        name,
                        server_name,
                    ))
            except Exception as error:
                log.debug('Intolerant for version {0} (got error: {1})'.format(
                    name,
                    error,
                ))
                protocol_intolerance.append(name)

        protocols.sort()
        protocols.reverse()
        protocol_intolerance.sort()
        self.merge(dict(
            analysis=dict(
                protocols=protocols,
                protocol_intolerance=protocol_intolerance,
            )
        ))
示例#47
0
    def probe(self, address, certificates):
        '''
        Tests for the Heartbleed TLS attack which targets protocols that have
        the heartbeat extension enabled and do improper boundary checks, such
        as found in OpenSSL versions between 1.0.1 - 1.0.1f.

        Provides the following keys:

        * ``weakness.heartbleed``
        '''
        if address is None:
            raise Probe.Skip('offline; no address supplied')

        weakness = {}
        weakness['status'] = 'good'
        weakness['exists'] = False
        weakness['reason'] = 'Heartbeat not enabled'

        try:
            remote = socket.create_connection(address)
        except socket.error as error:
            raise Probe.Skip('Network error: {0}'.format(error))

        if remote:
            remote.send(TLS_HELLO)
            while True:
                try:
                    typ, version, payload = self._get_msg(remote)
                except ValueError as error:
                    log.debug('Oops: {0}'.format(error))
                    remote = None
                    break
                else:
                    if typ == 22 and ord(payload[0]) == 0x0e:
                        break

        if remote:
            remote.send(TLS_HEARTBEAT)
            while True:
                remote.send(TLS_HEARTBEAT)
                try:
                    typ, version, payload = self._get_msg(remote)
                except ValueError as error:
                    log.debug('Oops: {0}'.format(error))
                    break
                else:
                    if typ == 24:
                        log.debug('Received heartbeat response')
                        if len(payload) > 3:
                            weakness['status'] = 'error'
                            weakness['exists'] = True
                            weakness['reason'] = 'Server returned more data ' +\
                                                 'than it should have'
                        else:
                            weakness['reason'] = 'Server-side fixed'

                        break

                    elif typ == 21:
                        # Alert
                        break

            remote.close()

        self.merge(dict(weakness=dict(heartbleed=weakness)))
    def check_trust(self, certificate):
        '''
        Check if the certificate provided in ``certificate`` is trusted by:

        1. checking if the certificate has a trust anchor in our trust store, if
           so, check the certificate validity with the trust store
        2. checking if the previous certificate was trusted and is a
           certificate authority, if so, check the certificate validity with the
           previously provided certificate in the chain
        3. checking if the certificate is self signed
        '''
        log.debug('Analyzing {0}'.format(certificate.get_subject_str()))

        subject_hash = certificate.get_subject_hash()
        issuer = certificate.get_issuer()
        issuer_hash = certificate.get_issuer_hash()
        issuer_name = issuer.get('commonName',
                                 issuer.get('organizationName', issuer_hash))
        trusted = (subject_hash in TRUST_STORE)

        if subject_hash not in self.chain_hash:
            self.chain.append(certificate)
            self.chain_hash.append(subject_hash)

        if self.trust and self.trust[-1]['status'] != 'good':
            yield dict(
                status='error',
                reason='Invalid chain',
            )
            return

        # Self-signed certificate
        if subject_hash == issuer_hash:
            if subject_hash in TRUST_STORE:
                # Certificate should be able to verify itself
                issuer = TRUST_STORE[subject_hash]
                status = issuer.verify(certificate)
                if status is True:
                    log.debug('Issuer "{0}" in trust store'.format(
                        issuer_name, ))
                    yield dict(
                        status='good',
                        reason='In trust store',
                    )
                    return

                elif status is None:
                    yield dict(
                        status='unknown',
                        reason='Unable to verify (local issue)',
                    )

            # Untrusted self-signed certificate
            log.debug('Self-signed certificate in chain')
            yield dict(trust=dict(
                status='error',
                reason='Self-signed certificate in chain',
            ))
            return

        also_check = []
        if issuer_hash in TRUST_STORE:
            issuer = TRUST_STORE[issuer_hash]
            log.debug('Issuer "{0}" in trust store'.format(issuer_name))
            if issuer_hash not in self.chain_hash:
                also_check.append(issuer)

            if issuer.verify(certificate):
                yield dict(
                    status='good',
                    reason='In trust store',
                )
            else:
                yield dict(
                    status='error',
                    reason='Verification failed',
                )

        elif issuer_hash in self.chain_hash:
            log.debug('Issuer {0} in trust chain'.format(issuer_name))
            issuer = self.chain[self.chain_hash.index(issuer_hash)]
            if issuer.verify(certificate):
                yield dict(
                    status='good',
                    reason='In trust chain',
                )
            else:
                yield dict(
                    status='error',
                    reason='Verification failed',
                )

        else:
            yield dict(
                status='error',
                reason='Issuer {0} unknown'.format(issuer_name),
            )

        for check in also_check:
            for trust in self.check_trust(check):
                yield trust
示例#49
0
    def _recv_message(self, expected_type, secondary_type=None,
                      constructor_type=None):

        if not isinstance(expected_type, tuple):
            expected_type= (expected_type,)

        while True:
            for result in self._recv_next_record():
                if result in (0, 1):
                    yield result
            record_header, r = result
            content_type = record_header.content_type

            log.debug('Received {0} record'.format(
                TLS_CONTENT_TYPE.get(content_type, content_type)
            ))
            if content_type == ContentType.application_data:
                if r.pos == len(r):
                    continue

            if content_type not in expected_type:
                if content_type == ContentType.alert:
                    Alert().parse(r).throw()

                raise ValueError(
                    'Unexpected record type {0}, expected {1}'.format(
                        TLS_CONTENT_TYPE.get(content_type, content_type),
                        map(TLS_CONTENT_TYPE.get, expected_type)
                    )
                )

            # Parse based on content_type
            if content_type == ContentType.alert:
                yield Alert().parse(r)

            elif content_type == ContentType.change_cipher_spec:
                yield ChangeCipherSpec().parse(r)

            elif content_type == ContentType.handshake:
                if not isinstance(secondary_type, tuple):
                    secondary_type = (secondary_type,)

                if record_header.v2:
                    sub_type = r.get(1)
                    if sub_type != HandshakeType.client_hello:
                        raise TypeError('Expected client hello')
                    if HandshakeType.client_hello not in secondary_type:
                        raise TypeError('Unexpected message')
                    sub_type = HandshakeType.client_hello

                else:
                    sub_type = r.get(1)
                    if sub_type not in secondary_type:
                        raise TypeError(
                            'Unexpected message {0} ({1}), '
                            'expected {2}/{3}'.format(
                                sub_type,
                                TLS_HANDSHAKE_TYPE.get(sub_type, 'unknown'),
                                map(TLS_HANDSHAKE_TYPE.get, expected_type),
                                map(TLS_HANDSHAKE_TYPE.get, secondary_type)
                            )
                        )

                log.debug('... with sub type {0}'.format(
                    TLS_HANDSHAKE_TYPE.get(sub_type, sub_type)
                ))

                if sub_type == HandshakeType.client_hello:
                    yield ClientHello(record_header.v2).parse(r)

                elif sub_type == HandshakeType.server_hello:
                    yield ServerHello(record_header.v2).parse(r)

                elif sub_type == HandshakeType.certificate:
                    yield Certificate(constructor_type).parse(r)

                elif sub_type == HandshakeType.certificate_status:
                    yield CertificateStatus().parse(r)

                elif sub_type == HandshakeType.server_key_exchange:
                    yield ServerKeyExchange(constructor_type).parse(r)

                elif sub_type == HandshakeType.server_hello_done:
                    yield ServerHelloDone().parse(r)

                else:
                    raise AssertionError(TLS_HANDSHAKE_TYPE.get(sub_type, sub_type))
示例#50
0
文件: base.py 项目: tehmaze/tlsspy
    def get_host_name(self, host, port=0):
        # Try IPv6 resolving
        try:
            socket.inet_pton(socket.AF_INET6, host)
            if have_dns:
                try:
                    name = reversename.from_address(host)
                    name = str(resolver.query(name, 'PTR')[0]).rstrip('.')
                    log.debug('{0} resolved to {1}'.format(host, name))
                    return name
                except Exception as error:
                    log.debug('{0} failed to resolve: {1}'.format(host, error))
                    return host
            else:
                return host
        except socket.error:
            pass

        # Try IPv4 resolving
        try:
            socket.inet_pton(socket.AF_INET, host)
            if have_dns:
                try:
                    name = reversename.from_address(host)
                    name = str(resolver.query(name, 'PTR')[0]).rstrip('.')
                    log.debug('{0} resolved to {1}'.format(host, name))
                    return name
                except Exception as error:
                    log.debug('{0} failed to resolve: {1}'.format(host, error))
                    return host
            else:
                name = socket.gethostbyaddr(host)[0]
                if name:
                    log.debug('{0} resolved to {1}'.format(host, name))
                    return name
        except socket.error:
            pass

        # Give up
        log.debug('{0} failed to resolve: not an IP'.format(host))
        return host
示例#51
0
    def probe(self, address, certificates):
        '''
        Analyze the cipher suites supported by the server. Also try to establish
        if the server has a preferred cipher order.

        Provides the following keys:

        * ``analysis.ciphers``
        * ``analysis.features``
        * ``ciphers``
        '''
        if address is None:
            raise Probe.Skip('offline; no address supplied')

        # Features
        features = {}
        features['forward_secrecy'] = False  # Detect later
        features['preferred_order'] = False  # Detect later

        # Enlist all ciphers, start with NULL cipher first
        all_cipher_suites = TLS_CIPHER_SUITE.keys()
        all_cipher_suites.sort()

        # Remove our pseudo-cipher, it's added later in the check
        all_cipher_suites.remove(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)

        # Test what cipher the server selects
        cipher_suite1 = self._test_cipher(address, *all_cipher_suites)
        if cipher_suite1 is None:
            log.error('No cipher suite selected by server?!')
            return

        # Now test what cipher the server selects next
        all_cipher_suites.remove(cipher_suite1)
        cipher_suite2 = self._test_cipher(address, *all_cipher_suites)
        if cipher_suite2 is None:
            log.error('No cipher suite selected by server?!')
            return

        # Now that we have two ciphers, offer them in reverse order. If the
        # server selects cipher1 again, the server has a preferred order.
        all_cipher_suites = [
            cipher_suite2,
            cipher_suite1,
        ]
        if self._test_cipher(address, *all_cipher_suites) == cipher_suite1:
            order = True
        else:
            order = False

        # Store feature
        features['preferred_order'] = order

        # Start bulk-testing ciphers
        all_cipher_suites = TLS_CIPHER_SUITE.keys()
        all_cipher_suites.sort()
        all_cipher_suites.remove(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
        all_cipher_suites.remove(cipher_suite1)
        all_cipher_suites.remove(cipher_suite2)
        our_cipher_suites = [
            cipher_suite1,
            cipher_suite2,
        ]

        if order:
            log.info('Serial scanning {0} suites in server order'.format(
                len(all_cipher_suites), ))
            while all_cipher_suites:
                try:
                    cipher_suite = self._test_cipher(address,
                                                     *all_cipher_suites)
                except Exception as error:
                    log.debug('None of our suites are accepted, stopping')
                    cipher_suite = None

                if cipher_suite is None:
                    break
                else:
                    our_cipher_suites.append(cipher_suite)
                    all_cipher_suites.remove(cipher_suite)

        else:
            # If there is no server-preferred order, we can use parallel
            # (threaded) probing
            parallel = self.config.get('parallel', 0)
            if parallel:
                log.info('Parallel scanning {0} suites'.format(
                    len(all_cipher_suites), ))
                pool = ThreadPool()

                for cipher_suite in reversed(all_cipher_suites):
                    pool.add_job(self._test_cipher, (address, cipher_suite))

                pool.start(parallel)
                for cipher_suite in pool.get_results():
                    if cipher_suite is not None:
                        our_cipher_suites.append(cipher_suite)

                pool.join()

            else:
                log.info('Serial scanning {0} suites'.format(
                    len(all_cipher_suites), ))
                for cipher_suite in reversed(all_cipher_suites):
                    if self._test_cipher(address, cipher_suite):
                        our_cipher_suites.append(cipher_suite)

        # Post-processing
        log.debug('Discovered {0} usable cipher suites'.format(
            len(our_cipher_suites), ))
        cipher_names = []
        support = []
        for cipher in our_cipher_suites:
            name, info = get_cipher_info(cipher)
            cipher_names.append(name)

            if info['encryption'] is None:
                info.update(
                    dict(status='error', reason='Cipher offers no encryption'))

            elif info['authentication'] is None:
                info.update(
                    dict(status='error',
                         reason='Cipher offers no authentication'))

            elif info['encryption'] in ('DES', 'DES40', 'IDEA'):
                info.update(dict(
                    status='error',
                    reason='Weak encryption',
                ))

            elif info['encryption_bits'] < 112:
                info.update(
                    dict(status='error',
                         reason='Cipher offers weak encryption, only {0} bits'.
                         format(info['encryption_bits'], )))

            elif info['encryption_bits'] < 128:
                info.update(
                    dict(status='warning',
                         reason='Cipher offers weak encryption, only {0} bits'.
                         format(info['encryption_bits'], )))

            elif info['protocol'] == 'SSL':
                info.update(
                    dict(
                        status='error',
                        reason='Cipher uses weak SSL implementation',
                    ))

            else:
                if info['key_exchange'] in ('DHE', 'ECDHE'):
                    features['forward_secrecy'] = True
                info['status'] = 'good'

            support.append({name: info})

        self.merge(
            dict(
                analysis=dict(
                    ciphers=support,
                    features=features,
                ),
                ciphers=cipher_names,
            ))