Exemplo n.º 1
0
    def createBasicTicket(self):
        if self.__options.request is True:
            if self.__domain == self.__server:
                logging.info('Requesting TGT to target domain to use as basis')
            else:
                logging.info(
                    'Requesting TGT/TGS to target domain to use as basis')

            if self.__options.hashes is not None:
                lmhash, nthash = self.__options.hashes.split(':')
            else:
                lmhash = ''
                nthash = ''
            userName = Principal(self.__options.user,
                                 type=PrincipalNameType.NT_PRINCIPAL.value)
            tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                userName, self.__password, self.__domain, unhexlify(lmhash),
                unhexlify(nthash), None, self.__options.dc_ip)
            if self.__domain == self.__server:
                kdcRep = decoder.decode(tgt, asn1Spec=AS_REP())[0]
            else:
                serverName = Principal(
                    self.__options.spn,
                    type=PrincipalNameType.NT_SRV_INST.value)
                tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                    serverName, self.__domain, None, tgt, cipher, sessionKey)
                kdcRep = decoder.decode(tgs, asn1Spec=TGS_REP())[0]

            # Let's check we have all the necessary data based on the ciphers used. Boring checks
            ticketCipher = int(kdcRep['ticket']['enc-part']['etype'])
            encPartCipher = int(kdcRep['enc-part']['etype'])

            if (ticketCipher == EncryptionTypes.rc4_hmac.value or encPartCipher == EncryptionTypes.rc4_hmac.value) and \
                            self.__options.nthash is None:
                logging.critical(
                    'rc4_hmac is used in this ticket and you haven\'t specified the -nthash parameter. '
                    'Can\'t continue ( or try running again w/o the -request option)'
                )
                return None, None

            if (ticketCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value or
                encPartCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value) and \
                self.__options.aesKey is None:
                logging.critical(
                    'aes128_cts_hmac_sha1_96 is used in this ticket and you haven\'t specified the -aesKey parameter. '
                    'Can\'t continue (or try running again w/o the -request option)'
                )
                return None, None

            if (ticketCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value or
                encPartCipher == EncryptionTypes.aes128_cts_hmac_sha1_96.value) and \
                self.__options.aesKey is not None and len(self.__options.aesKey) > 32:
                logging.critical(
                    'aes128_cts_hmac_sha1_96 is used in this ticket and the -aesKey you specified is not aes128. '
                    'Can\'t continue (or try running again w/o the -request option)'
                )
                return None, None

            if (ticketCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value
                    or encPartCipher
                    == EncryptionTypes.aes256_cts_hmac_sha1_96.value
                ) and self.__options.aesKey is None:
                logging.critical(
                    'aes256_cts_hmac_sha1_96 is used in this ticket and you haven\'t specified the -aesKey parameter. '
                    'Can\'t continue (or try running again w/o the -request option)'
                )
                return None, None

            if ( ticketCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value or
                 encPartCipher == EncryptionTypes.aes256_cts_hmac_sha1_96.value) and \
                 self.__options.aesKey is not None and len(self.__options.aesKey) < 64:
                logging.critical(
                    'aes256_cts_hmac_sha1_96 is used in this ticket and the -aesKey you specified is not aes256. '
                    'Can\'t continue')
                return None, None
            kdcRep['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
            kdcRep['cname']['name-string'] = noValue
            kdcRep['cname']['name-string'][0] = self.__target

        else:
            logging.info('Creating basic skeleton ticket and PAC Infos')
            if self.__domain == self.__server:
                kdcRep = AS_REP()
                kdcRep['msg-type'] = ApplicationTagNumbers.AS_REP.value
            else:
                kdcRep = TGS_REP()
                kdcRep['msg-type'] = ApplicationTagNumbers.TGS_REP.value
            kdcRep['pvno'] = 5
            if self.__options.nthash is None:
                kdcRep['padata'] = noValue
                kdcRep['padata'][0] = noValue
                kdcRep['padata'][0][
                    'padata-type'] = PreAuthenticationDataTypes.PA_ETYPE_INFO2.value

                etype2 = ETYPE_INFO2()
                etype2[0] = noValue
                if len(self.__options.aesKey) == 64:
                    etype2[0][
                        'etype'] = EncryptionTypes.aes256_cts_hmac_sha1_96.value
                else:
                    etype2[0][
                        'etype'] = EncryptionTypes.aes128_cts_hmac_sha1_96.value
                etype2[0]['salt'] = '%s%s' % (self.__domain.upper(),
                                              self.__target)
                encodedEtype2 = encoder.encode(etype2)

                kdcRep['padata'][0]['padata-value'] = encodedEtype2

            kdcRep['crealm'] = self.__domain.upper()
            kdcRep['cname'] = noValue
            kdcRep['cname']['name-type'] = PrincipalNameType.NT_PRINCIPAL.value
            kdcRep['cname']['name-string'] = noValue
            kdcRep['cname']['name-string'][0] = self.__target

            kdcRep['ticket'] = noValue
            kdcRep['ticket']['tkt-vno'] = ProtocolVersionNumber.pvno.value
            kdcRep['ticket']['realm'] = self.__domain.upper()
            kdcRep['ticket']['sname'] = noValue
            kdcRep['ticket']['sname']['name-string'] = noValue
            kdcRep['ticket']['sname']['name-string'][0] = self.__service

            if self.__domain == self.__server:
                kdcRep['ticket']['sname'][
                    'name-type'] = PrincipalNameType.NT_SRV_INST.value
                kdcRep['ticket']['sname']['name-string'][
                    1] = self.__domain.upper()
            else:
                kdcRep['ticket']['sname'][
                    'name-type'] = PrincipalNameType.NT_PRINCIPAL.value
                kdcRep['ticket']['sname']['name-string'][1] = self.__server

            kdcRep['ticket']['enc-part'] = noValue
            kdcRep['ticket']['enc-part']['kvno'] = 2
            kdcRep['enc-part'] = noValue
            if self.__options.nthash is None:
                if len(self.__options.aesKey) == 64:
                    kdcRep['ticket']['enc-part'][
                        'etype'] = EncryptionTypes.aes256_cts_hmac_sha1_96.value
                    kdcRep['enc-part'][
                        'etype'] = EncryptionTypes.aes256_cts_hmac_sha1_96.value
                else:
                    kdcRep['ticket']['enc-part'][
                        'etype'] = EncryptionTypes.aes128_cts_hmac_sha1_96.value
                    kdcRep['enc-part'][
                        'etype'] = EncryptionTypes.aes128_cts_hmac_sha1_96.value
            else:
                kdcRep['ticket']['enc-part'][
                    'etype'] = EncryptionTypes.rc4_hmac.value
                kdcRep['enc-part']['etype'] = EncryptionTypes.rc4_hmac.value

            kdcRep['enc-part']['kvno'] = 2
            kdcRep['enc-part']['cipher'] = noValue

        pacInfos = self.createBasicPac(kdcRep)

        return kdcRep, pacInfos
Exemplo n.º 2
0
            else:
                raise
        else:
            raise

    # This should be the PREAUTH_FAILED packet

    asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0]
    methods = decoder.decode(str(asRep['e-data']), asn1Spec=METHOD_DATA())[0]
    salt = ''
    encryptionTypesData = dict()
    for method in methods:
        if method[
                'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
            etypes2 = decoder.decode(str(method['padata-value']),
                                     asn1Spec=ETYPE_INFO2())[0]
            for etype2 in etypes2:
                if etype2['salt'] is None:
                    salt = ''
                else:
                    salt = str(etype2['salt'])
                encryptionTypesData[etype2['etype']] = salt
        elif method[
                'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value:
            etypes = decoder.decode(str(method['padata-value']),
                                    asn1Spec=ETYPE_INFO())[0]
            for etype in etypes:
                if etype['salt'] is None:
                    salt = ''
                else:
                    salt = str(etype['salt'])
Exemplo n.º 3
0
def getKerberosTGT(clientName,
                   password,
                   domain,
                   lmhash,
                   nthash,
                   aesKey='',
                   kdcHost=None,
                   requestPAC=True):

    # Convert to binary form, just in case we're receiving strings
    if isinstance(lmhash, str):
        try:
            lmhash = unhexlify(lmhash)
        except TypeError:
            pass
    if isinstance(nthash, str):
        try:
            nthash = unhexlify(nthash)
        except TypeError:
            pass
    if isinstance(aesKey, str):
        try:
            aesKey = unhexlify(aesKey)
        except TypeError:
            pass

    asReq = AS_REQ()

    domain = domain.upper()
    serverName = Principal('krbtgt/%s' % domain,
                           type=constants.PrincipalNameType.NT_PRINCIPAL.value)

    pacRequest = KERB_PA_PAC_REQUEST()
    pacRequest['include-pac'] = requestPAC
    encodedPacRequest = encoder.encode(pacRequest)

    asReq['pvno'] = 5
    asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)

    asReq['padata'] = noValue
    asReq['padata'][0] = noValue
    asReq['padata'][0]['padata-type'] = int(
        constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value)
    asReq['padata'][0]['padata-value'] = encodedPacRequest

    reqBody = seq_set(asReq, 'req-body')

    opts = list()
    opts.append(constants.KDCOptions.forwardable.value)
    opts.append(constants.KDCOptions.renewable.value)
    opts.append(constants.KDCOptions.proxiable.value)
    reqBody['kdc-options'] = constants.encodeFlags(opts)

    seq_set(reqBody, 'sname', serverName.components_to_asn1)
    seq_set(reqBody, 'cname', clientName.components_to_asn1)

    if domain == '':
        raise Exception('Empty Domain not allowed in Kerberos')

    reqBody['realm'] = domain

    now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
    reqBody['till'] = KerberosTime.to_asn1(now)
    reqBody['rtime'] = KerberosTime.to_asn1(now)
    reqBody['nonce'] = rand.getrandbits(31)

    # Yes.. this shouldn't happen but it's inherited from the past
    if aesKey is None:
        aesKey = b''

    if nthash == b'':
        # This is still confusing. I thought KDC_ERR_ETYPE_NOSUPP was enough,
        # but I found some systems that accepts all ciphers, and trigger an error
        # when requesting subsequent TGS :(. More research needed.
        # So, in order to support more than one cypher, I'm setting aes first
        # since most of the systems would accept it. If we're lucky and
        # KDC_ERR_ETYPE_NOSUPP is returned, we will later try rc4.
        if aesKey != b'':
            if len(aesKey) == 32:
                supportedCiphers = (int(
                    constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), )
            else:
                supportedCiphers = (int(
                    constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value), )
        else:
            supportedCiphers = (int(
                constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), )
    else:
        # We have hashes to try, only way is to request RC4 only
        supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value), )

    seq_set_iter(reqBody, 'etype', supportedCiphers)

    message = encoder.encode(asReq)

    try:
        r = sendReceive(message, domain, kdcHost)
    except KerberosError as e:
        if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
            if supportedCiphers[0] in (
                    constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value,
                    constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value
            ) and aesKey == b'':
                supportedCiphers = (int(
                    constants.EncryptionTypes.rc4_hmac.value), )
                seq_set_iter(reqBody, 'etype', supportedCiphers)
                message = encoder.encode(asReq)
                r = sendReceive(message, domain, kdcHost)
            else:
                raise
        else:
            raise

    # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the
    # 'Do not require Kerberos preauthentication' set
    preAuth = True
    try:
        asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0]
    except:
        # Most of the times we shouldn't be here, is this a TGT?
        asRep = decoder.decode(r, asn1Spec=AS_REP())[0]
        # Yes
        preAuth = False

    encryptionTypesData = dict()
    salt = ''
    if preAuth is False:
        # In theory, we should have the right credentials for the etype specified before.
        methods = asRep['padata']
        encryptionTypesData[supportedCiphers[
            0]] = salt  # handle RC4 fallback, we don't need any salt
        tgt = r
    else:
        methods = decoder.decode(asRep['e-data'], asn1Spec=METHOD_DATA())[0]

    for method in methods:
        if method[
                'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
            etypes2 = decoder.decode(method['padata-value'],
                                     asn1Spec=ETYPE_INFO2())[0]
            for etype2 in etypes2:
                try:
                    if etype2['salt'] is None or etype2['salt'].hasValue(
                    ) is False:
                        salt = ''
                    else:
                        salt = etype2['salt'].prettyPrint()
                except PyAsn1Error:
                    salt = ''

                encryptionTypesData[etype2['etype']] = b(salt)
        elif method[
                'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value:
            etypes = decoder.decode(method['padata-value'],
                                    asn1Spec=ETYPE_INFO())[0]
            for etype in etypes:
                try:
                    if etype['salt'] is None or etype['salt'].hasValue(
                    ) is False:
                        salt = ''
                    else:
                        salt = etype['salt'].prettyPrint()
                except PyAsn1Error:
                    salt = ''

                encryptionTypesData[etype['etype']] = b(salt)

    enctype = supportedCiphers[0]

    cipher = _enctype_table[enctype]

    # Pass the hash/aes key :P
    if nthash != b'' and (isinstance(nthash, bytes) and nthash != b''):
        key = Key(cipher.enctype, nthash)
    elif aesKey != b'':
        key = Key(cipher.enctype, aesKey)
    else:
        key = cipher.string_to_key(password, encryptionTypesData[enctype],
                                   None)

    if preAuth is True:
        if enctype in encryptionTypesData is False:
            raise Exception('No Encryption Data Available!')

        # Let's build the timestamp
        timeStamp = PA_ENC_TS_ENC()

        now = datetime.datetime.utcnow()
        timeStamp['patimestamp'] = KerberosTime.to_asn1(now)
        timeStamp['pausec'] = now.microsecond

        # Encrypt the shyte
        encodedTimeStamp = encoder.encode(timeStamp)

        # Key Usage 1
        # AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the
        # client key (Section 5.2.7.2)
        encriptedTimeStamp = cipher.encrypt(key, 1, encodedTimeStamp, None)

        encryptedData = EncryptedData()
        encryptedData['etype'] = cipher.enctype
        encryptedData['cipher'] = encriptedTimeStamp
        encodedEncryptedData = encoder.encode(encryptedData)

        # Now prepare the new AS_REQ again with the PADATA
        # ToDo: cannot we reuse the previous one?
        asReq = AS_REQ()

        asReq['pvno'] = 5
        asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value)

        asReq['padata'] = noValue
        asReq['padata'][0] = noValue
        asReq['padata'][0]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value)
        asReq['padata'][0]['padata-value'] = encodedEncryptedData

        asReq['padata'][1] = noValue
        asReq['padata'][1]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value)
        asReq['padata'][1]['padata-value'] = encodedPacRequest

        reqBody = seq_set(asReq, 'req-body')

        opts = list()
        opts.append(constants.KDCOptions.forwardable.value)
        opts.append(constants.KDCOptions.renewable.value)
        opts.append(constants.KDCOptions.proxiable.value)
        reqBody['kdc-options'] = constants.encodeFlags(opts)

        seq_set(reqBody, 'sname', serverName.components_to_asn1)
        seq_set(reqBody, 'cname', clientName.components_to_asn1)

        reqBody['realm'] = domain

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)
        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['rtime'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = rand.getrandbits(31)

        seq_set_iter(reqBody, 'etype', ((int(cipher.enctype), )))

        try:
            tgt = sendReceive(encoder.encode(asReq), domain, kdcHost)
        except Exception as e:
            if str(e).find('KDC_ERR_ETYPE_NOSUPP') >= 0:
                if lmhash == b'' and nthash == b'' and (aesKey == b''
                                                        or aesKey is None):
                    from impacket.ntlm import compute_lmhash, compute_nthash
                    lmhash = compute_lmhash(password)
                    nthash = compute_nthash(password)
                    return getKerberosTGT(clientName, password, domain, lmhash,
                                          nthash, aesKey, kdcHost, requestPAC)
            raise

        asRep = decoder.decode(tgt, asn1Spec=AS_REP())[0]

    # So, we have the TGT, now extract the new session key and finish
    cipherText = asRep['enc-part']['cipher']

    if preAuth is False:
        # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it.
        LOG.debug('$krb5asrep$%d$%s@%s:%s$%s' %
                  (asRep['enc-part']['etype'], clientName, domain,
                   hexlify(asRep['enc-part']['cipher'].asOctets()[:16]),
                   hexlify(asRep['enc-part']['cipher'].asOctets()[16:])))
    # Key Usage 3
    # AS-REP encrypted part (includes TGS session key or
    # application session key), encrypted with the client key
    # (Section 5.4.2)
    try:
        plainText = cipher.decrypt(key, 3, cipherText)
    except InvalidChecksum as e:
        # probably bad password if preauth is disabled
        if preAuth is False:
            error_msg = "failed to decrypt session key: %s" % str(e)
            raise SessionKeyDecryptionError(error_msg, asRep, cipher, key,
                                            cipherText)
        raise
    encASRepPart = decoder.decode(plainText, asn1Spec=EncASRepPart())[0]

    # Get the session key and the ticket
    cipher = _enctype_table[encASRepPart['key']['keytype']]
    sessionKey = Key(cipher.enctype,
                     encASRepPart['key']['keyvalue'].asOctets())

    # ToDo: Check Nonces!

    return tgt, cipher, key, sessionKey
        # Yes
        preAuth = False

    encryptionTypesData = dict()
    salt = ''
    if preAuth is False:
        # In theory, we should have the right credentials for the etype specified before.
        methods = asRep['padata']
        encryptionTypesData[supportedCiphers[0]] = salt # handle RC4 fallback, we don't need any salt
        tgt = r
    else:
        methods = decoder.decode(str(asRep['e-data']), asn1Spec=METHOD_DATA())[0]

    for method in methods:
        if method['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
            etypes2 = decoder.decode(str(method['padata-value']), asn1Spec = ETYPE_INFO2())[0]
            for etype2 in etypes2:
                try:
                    if etype2['salt'] is None or etype2['salt'].hasValue() is False:
                        salt = ''
                    else:
                        salt = str(etype2['salt'])
                except PyAsn1Error, e:
                    salt = ''

                encryptionTypesData[etype2['etype']] = salt
        elif method['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value:
            etypes = decoder.decode(str(method['padata-value']), asn1Spec = ETYPE_INFO())[0]
            for etype in etypes:
                try:
                    if etype['salt'] is None or etype['salt'].hasValue() is False: