def test_parse_public_key(self):
        expected = server_pub_key_token

        actual = TSRequest()
        actual.parse_data(public_key_ts_request)

        assert expected == actual['pub_key_auth'].value
    def test_create_credential_ts_request(self):
        expected = credential_ts_request

        actual = TSRequest()
        actual['auth_info'].value = credentials_encrypted_password_creds

        assert expected == actual.get_data()
    def test_get_empty_field(self):
        with self.assertRaises(AsnStructureException) as context:
            actual = TSRequest()
            actual.parse_data(credential_ts_request)
            actual['fake_field']

        assert context.exception.args[
            0] == 'Illegal field fake_field in ASN.1 structure'
    def test_parse_ts_request_wrong_type(self):
        test_data = hex_to_byte('A0 00 00 00 00')

        with self.assertRaises(AsnStructureException) as context:
            test_ts_request = TSRequest()
            test_ts_request.parse_data(test_data)

        assert context.exception.args[
            0] == 'Expecting TSRequest type to be (30), was (a0)'
    def test_parse_ts_request_invalid_sequence(self):
        test_data = hex_to_byte('30 13 A0 03 02 01 03 A6 03 02 01 01')

        with self.assertRaises(AsnStructureException) as context:
            test_ts_request = TSRequest()
            test_ts_request.parse_data(test_data)

        assert context.exception.args[
            0] == 'Unknown sequence byte (a6) in sequence'
    def test_create_negotiate_ts_request(self):
        expected = negotiate_ts_request

        actual_nego_data = NegoData()
        actual_nego_data['nego_token'].value = negotiate_token
        actual = TSRequest()
        actual['nego_tokens'].value = actual_nego_data.get_data()

        assert expected == actual.get_data()
    def test_parse_challenge_ts_request(self):
        expected = challenge_token

        actual_ts_request = TSRequest()
        actual_ts_request.parse_data(challenge_ts_request)

        actual = NegoData()
        actual.parse_data(actual_ts_request['nego_tokens'].value)

        assert expected == actual['nego_token'].value
Esempio n. 8
0
    def test_verify_public_key_good(self, mock_unwrap):
        test_credssp_context = HttpCredSSPAuth('', '')
        test_ntlm_context = ntlm.Ntlm()
        test_ntlm_context.session_security = session_security.SessionSecurity(1, 'key'.encode())
        test_credssp_context.context = test_ntlm_context
        test_ts_request = TSRequest()
        test_ts_request.parse_data(public_key_ts_request)
        test_public_key = hex_to_byte('00') + server_pub_key_token[1:]

        test_credssp_context._verify_public_keys(test_public_key, test_ts_request)
    def test_create_authenticate_ts_request(self):
        expected = auth_ts_request

        actual_nego_data = NegoData()
        actual_nego_data['nego_token'].value = auth_token

        actual = TSRequest()
        actual['nego_tokens'].value = actual_nego_data.get_data()
        actual['pub_key_auth'].value = pub_key_token

        assert expected == actual.get_data()
Esempio n. 10
0
    def test_verify_public_key_invalid(self, mock_unwrap):
        test_credssp_context = HttpCredSSPAuth('', '')
        test_ntlm_context = ntlm.Ntlm()
        test_ntlm_context.session_security = session_security.SessionSecurity(1, 'key'.encode())
        test_credssp_context.context = test_ntlm_context
        test_ts_request = TSRequest()
        test_ts_request.parse_data(public_key_ts_request)

        # Use the wrong first byte to ensure the keys don't match
        test_public_key = hex_to_byte('01') + server_pub_key_token[1:]

        with self.assertRaises(AssertionError) as context:
            test_credssp_context._verify_public_keys(test_public_key, test_ts_request)

        assert context.exception.args[0] == 'Could not verify key sent from the server, possibly man in the middle attack'
Esempio n. 11
0
    def _get_encrypted_credentials(self, context):
        """
        [MS-CSSP] 3.1.5 Processing Events and Sequencing Rules - Step 5
        https://msdn.microsoft.com/en-us/library/cc226791.aspx

        After the client has verified the server's authenticity, it encrypts
        the user's credentials with the authentication protocol's encryption
        services. The resulting value is encapsulated in the authInfo field of
        the TSRequest structure and sent over the encrypted TLS channel to the
        server

        :param context: The authenticated security context
        :return: The encrypted TSRequest that contains the user's credentials
        """
        domain = u""
        if "\\" in context.username:
            domain, username = context.username.split('\\', 1)
        else:
            username = context.username

        ts_password = TSPasswordCreds()
        ts_password['domainName'] = domain.encode('utf-16-le')
        ts_password['userName'] = username.encode('utf-16-le')
        ts_password['password'] = context.password.encode('utf-16-le')

        ts_credentials = TSCredentials()
        ts_credentials['credType'] = ts_password.CRED_TYPE
        ts_credentials['credentials'] = encoder.encode(ts_password)

        ts_request = TSRequest()
        enc_credentials = context.wrap(encoder.encode(ts_credentials)).data
        ts_request['authInfo'] = enc_credentials

        return encoder.encode(ts_request)
    def test_check_error_code_version_3_none(self):
        test_data = utils.hex_to_byte('30 09 A0 03 02 01 03')

        test_ts_request = TSRequest()
        test_ts_request.parse_data(test_data)
        test_ts_request.check_error_code()

        assert test_ts_request['error_code'].value == None
    def test_check_error_code_logon_failure(self):
        expected_byte = b'c000006d'
        test_data = hex_to_byte('30 13 A0 03 02 01 03 A4 06 02 04 C0 00 00 6D')

        with self.assertRaises(NTStatusException) as context:
            test_ts_request = TSRequest()
            test_ts_request.parse_data(test_data)
            test_ts_request.check_error_code()

        assert context.exception.args[
            0] == 'STATUS_LOGON_FAILURE - %s' % expected_byte
    def test_check_error_code_undefinied(self):
        expected_byte = b'c000006e'
        test_data = hex_to_byte('30 13 A0 03 02 01 03 A4 06 02 04 C0 00 00 6E')

        with self.assertRaises(NTStatusException) as context:
            test_ts_request = TSRequest()
            test_ts_request.parse_data(test_data)
            test_ts_request.check_error_code()

        assert context.exception.args[
            0] == 'NTSTATUS error: Not Defined %s' % expected_byte
Esempio n. 15
0
    def _build_pub_key_auth(self, context, nonce, auth_token, public_key):
        """
        [MS-CSSP] 3.1.5 Processing Events and Sequencing Rules - Step 3
        https://msdn.microsoft.com/en-us/library/cc226791.aspx

        This step sends the final SPNEGO token to the server if required and
        computes the value for the pubKeyAuth field for the protocol version
        negotiated.

        The format of the pubKeyAuth field depends on the version that the
        server supports.

        For version 2 to 4:
        The pubKeyAuth field is just wrapped using the authenticated context

        For versions 5 to 6:
        The pubKeyAuth is a sha256 hash of the server's public key plus a nonce
        and a magic string value. This hash is wrapped using the authenticated
        context and the nonce is added to the TSRequest alongside the nonce
        used in the hash calcs.

        :param context: The authenticated context
        :param nonce: If versions 5+, the nonce to use in the hash
        :param auth_token: If NTLM, this is the last msg (authenticate msg) to
            send in the same request
        :param public_key: The server's public key
        :return: The TSRequest as a byte string to send to the server
        """
        ts_request = TSRequest()

        if auth_token is not None:
            nego_token = NegoToken()
            nego_token['negoToken'] = auth_token
            ts_request['negoTokens'].append(nego_token)

        if nonce is not None:
            ts_request['clientNonce'] = nonce
            hash_input = b"CredSSP Client-To-Server Binding Hash\x00" + nonce + public_key
            pub_value = hashlib.sha256(hash_input).digest()
        else:
            pub_value = public_key

        enc_public_key = context.wrap(pub_value).data
        ts_request['pubKeyAuth'] = enc_public_key

        return encoder.encode(ts_request)
Esempio n. 16
0
    def credssp_generator(self):
        """
        [MS-CSSP] 3.1.5 Processing Events and Sequencing Rules
        https://msdn.microsoft.com/en-us/library/cc226791.aspx

        Generator function that yields each CredSSP token to sent to the
        server. CredSSP has multiple steps that must be run for the client to
        successfully authenticate with the server and delegate the credentials.
        """
        log.debug("Starting TLS handshake process")
        self.tls_connection = SSL.Connection(self.tls_context)
        self.tls_connection.set_connect_state()

        while True:
            try:
                self.tls_connection.do_handshake()
            except SSL.WantReadError:
                out_token = self.tls_connection.bio_read(self.BIO_BUFFER_SIZE)

                log.debug("Step 1. TLS Handshake, returning token: %s" % binascii.hexlify(out_token))
                in_token = yield out_token, "Step 1. TLS Handshake"
                log.debug("Step 1. TLS Handshake, received token: %s" % binascii.hexlify(in_token))

                self.tls_connection.bio_write(in_token)
            else:
                break
        log.debug("TLS Handshake complete. Protocol: %s, Cipher: %s"
                  % (self.tls_connection.get_protocol_version_name(), self.tls_connection.get_cipher_name()))

        server_certificate = self.tls_connection.get_peer_certificate()
        server_public_key = self._get_subject_public_key(server_certificate)

        log.debug("Starting Authentication process")
        context = spnego.client(self.username, self.password, hostname=self.hostname, service='HTTP',
                                protocol=self.auth_mechanism)

        out_token = context.step()
        while True:
            nego_token = NegoToken()
            nego_token['negoToken'] = out_token

            ts_request = TSRequest()
            ts_request['negoTokens'].append(nego_token)
            ts_request_token = encoder.encode(ts_request)

            log.debug("Step 2. Authenticate, returning token: %s" % binascii.hexlify(ts_request_token))
            in_token = yield self.wrap(ts_request_token), "Step 2. Authenticate"
            in_token = self.unwrap(in_token)
            log.debug("Step 3. Authenticate, received token: %s" % binascii.hexlify(in_token))

            ts_request = decoder.decode(in_token, asn1Spec=TSRequest())[0]
            ts_request.check_error_code()
            version = int(ts_request['version'])
            out_token = context.step(bytes(ts_request['negoTokens'][0]['negoToken']))

            # Special edge case, we need to include the final NTLM token in the pubKeyAuth step but the context won't
            # be seen as complete at that stage so check if the known header is present.
            if context.complete or b"NTLMSSP\x00\x03\x00\x00\x00" in out_token:
                break

        version = min(version, TSRequest.CLIENT_VERSION)
        log.debug("Starting public key verification process at version %d" % version)
        if version < self.minimum_version:
            raise AuthenticationException("The reported server version was %d and did not meet the minimum "
                                          "requirements of %d" % (version, self.minimum_version))
        if version > 4:
            nonce = os.urandom(32)
        else:
            log.warning("Reported server version was %d, susceptible to MitM attacks and should be patched - "
                        "CVE 2018-0886" % version)
            nonce = None

        pub_key_auth = self._build_pub_key_auth(context, nonce, out_token, server_public_key)
        log.debug("Step 3. Server Authentication, returning token: %s" % binascii.hexlify(pub_key_auth))
        in_token = yield self.wrap(pub_key_auth), "Step 3. Server Authentication"
        in_token = self.unwrap(in_token)
        log.debug("Step 3. Server Authentication, received token: %s" % binascii.hexlify(in_token))

        log.debug("Starting server public key response verification")
        ts_request = decoder.decode(in_token, asn1Spec=TSRequest())[0]
        ts_request.check_error_code()
        if not ts_request['pubKeyAuth'].isValue:
            raise AuthenticationException("The server did not response with pubKeyAuth info, authentication was "
                                          "rejected")
        if len(ts_request['negoTokens']) > 0:
            # SPNEGO auth returned the mechListMIC for us to verify
            context.step(bytes(ts_request['negoTokens'][0]['negoToken']))

        response_key = context.unwrap(bytes(ts_request['pubKeyAuth'])).data
        self._verify_public_keys(nonce, response_key, server_public_key)

        log.debug("Sending encrypted credentials")
        enc_credentials = self._get_encrypted_credentials(context)

        yield self.wrap(enc_credentials), "Step 5. Delegate Credentials"
    def test_check_error_code_version_2_none(self):
        test_ts_request = TSRequest()
        test_ts_request.parse_data(challenge_ts_request)
        test_ts_request.check_error_code()

        assert test_ts_request['error_code'].value == None