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
else: raise else: raise # This should be the PREAUTH_FAILED packet asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0] methods = decoder.decode(str(asRep['e-data']), asn1Spec=METHOD_DATA())[0] salt = '' encryptionTypesData = dict() for method in methods: if method[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value: etypes2 = decoder.decode(str(method['padata-value']), asn1Spec=ETYPE_INFO2())[0] for etype2 in etypes2: if etype2['salt'] is None: salt = '' else: salt = str(etype2['salt']) encryptionTypesData[etype2['etype']] = salt elif method[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value: etypes = decoder.decode(str(method['padata-value']), asn1Spec=ETYPE_INFO())[0] for etype in etypes: if etype['salt'] is None: salt = '' else: salt = str(etype['salt'])
def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey='', kdcHost=None, requestPAC=True): # Convert to binary form, just in case we're receiving strings if isinstance(lmhash, str): try: lmhash = unhexlify(lmhash) except TypeError: pass if isinstance(nthash, str): try: nthash = unhexlify(nthash) except TypeError: pass if isinstance(aesKey, str): try: aesKey = unhexlify(aesKey) except TypeError: pass asReq = AS_REQ() domain = domain.upper() serverName = Principal('krbtgt/%s' % domain, type=constants.PrincipalNameType.NT_PRINCIPAL.value) pacRequest = KERB_PA_PAC_REQUEST() pacRequest['include-pac'] = requestPAC encodedPacRequest = encoder.encode(pacRequest) asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][0]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) if domain == '': raise Exception('Empty Domain not allowed in Kerberos') reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = rand.getrandbits(31) # Yes.. this shouldn't happen but it's inherited from the past if aesKey is None: aesKey = b'' if nthash == b'': # This is still confusing. I thought KDC_ERR_ETYPE_NOSUPP was enough, # but I found some systems that accepts all ciphers, and trigger an error # when requesting subsequent TGS :(. More research needed. # So, in order to support more than one cypher, I'm setting aes first # since most of the systems would accept it. If we're lucky and # KDC_ERR_ETYPE_NOSUPP is returned, we will later try rc4. if aesKey != b'': if len(aesKey) == 32: supportedCiphers = (int( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), ) else: supportedCiphers = (int( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value), ) else: supportedCiphers = (int( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value), ) else: # We have hashes to try, only way is to request RC4 only supportedCiphers = (int(constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) try: r = sendReceive(message, domain, kdcHost) except KerberosError as e: if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: if supportedCiphers[0] in ( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value, constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value ) and aesKey == b'': supportedCiphers = (int( constants.EncryptionTypes.rc4_hmac.value), ) seq_set_iter(reqBody, 'etype', supportedCiphers) message = encoder.encode(asReq) r = sendReceive(message, domain, kdcHost) else: raise else: raise # This should be the PREAUTH_FAILED packet or the actual TGT if the target principal has the # 'Do not require Kerberos preauthentication' set preAuth = True try: asRep = decoder.decode(r, asn1Spec=KRB_ERROR())[0] except: # Most of the times we shouldn't be here, is this a TGT? asRep = decoder.decode(r, asn1Spec=AS_REP())[0] # Yes preAuth = False encryptionTypesData = dict() salt = '' if preAuth is False: # In theory, we should have the right credentials for the etype specified before. methods = asRep['padata'] encryptionTypesData[supportedCiphers[ 0]] = salt # handle RC4 fallback, we don't need any salt tgt = r else: methods = decoder.decode(asRep['e-data'], asn1Spec=METHOD_DATA())[0] for method in methods: if method[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value: etypes2 = decoder.decode(method['padata-value'], asn1Spec=ETYPE_INFO2())[0] for etype2 in etypes2: try: if etype2['salt'] is None or etype2['salt'].hasValue( ) is False: salt = '' else: salt = etype2['salt'].prettyPrint() except PyAsn1Error: salt = '' encryptionTypesData[etype2['etype']] = b(salt) elif method[ 'padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value: etypes = decoder.decode(method['padata-value'], asn1Spec=ETYPE_INFO())[0] for etype in etypes: try: if etype['salt'] is None or etype['salt'].hasValue( ) is False: salt = '' else: salt = etype['salt'].prettyPrint() except PyAsn1Error: salt = '' encryptionTypesData[etype['etype']] = b(salt) enctype = supportedCiphers[0] cipher = _enctype_table[enctype] # Pass the hash/aes key :P if nthash != b'' and (isinstance(nthash, bytes) and nthash != b''): key = Key(cipher.enctype, nthash) elif aesKey != b'': key = Key(cipher.enctype, aesKey) else: key = cipher.string_to_key(password, encryptionTypesData[enctype], None) if preAuth is True: if enctype in encryptionTypesData is False: raise Exception('No Encryption Data Available!') # Let's build the timestamp timeStamp = PA_ENC_TS_ENC() now = datetime.datetime.utcnow() timeStamp['patimestamp'] = KerberosTime.to_asn1(now) timeStamp['pausec'] = now.microsecond # Encrypt the shyte encodedTimeStamp = encoder.encode(timeStamp) # Key Usage 1 # AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the # client key (Section 5.2.7.2) encriptedTimeStamp = cipher.encrypt(key, 1, encodedTimeStamp, None) encryptedData = EncryptedData() encryptedData['etype'] = cipher.enctype encryptedData['cipher'] = encriptedTimeStamp encodedEncryptedData = encoder.encode(encryptedData) # Now prepare the new AS_REQ again with the PADATA # ToDo: cannot we reuse the previous one? asReq = AS_REQ() asReq['pvno'] = 5 asReq['msg-type'] = int(constants.ApplicationTagNumbers.AS_REQ.value) asReq['padata'] = noValue asReq['padata'][0] = noValue asReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_ENC_TIMESTAMP.value) asReq['padata'][0]['padata-value'] = encodedEncryptedData asReq['padata'][1] = noValue asReq['padata'][1]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_PAC_REQUEST.value) asReq['padata'][1]['padata-value'] = encodedPacRequest reqBody = seq_set(asReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.proxiable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) seq_set(reqBody, 'cname', clientName.components_to_asn1) reqBody['realm'] = domain now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['rtime'] = KerberosTime.to_asn1(now) reqBody['nonce'] = rand.getrandbits(31) seq_set_iter(reqBody, 'etype', ((int(cipher.enctype), ))) try: tgt = sendReceive(encoder.encode(asReq), domain, kdcHost) except Exception as e: if str(e).find('KDC_ERR_ETYPE_NOSUPP') >= 0: if lmhash == b'' and nthash == b'' and (aesKey == b'' or aesKey is None): from impacket.ntlm import compute_lmhash, compute_nthash lmhash = compute_lmhash(password) nthash = compute_nthash(password) return getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey, kdcHost, requestPAC) raise asRep = decoder.decode(tgt, asn1Spec=AS_REP())[0] # So, we have the TGT, now extract the new session key and finish cipherText = asRep['enc-part']['cipher'] if preAuth is False: # Let's output the TGT enc-part/cipher in John format, in case somebody wants to use it. LOG.debug('$krb5asrep$%d$%s@%s:%s$%s' % (asRep['enc-part']['etype'], clientName, domain, hexlify(asRep['enc-part']['cipher'].asOctets()[:16]), hexlify(asRep['enc-part']['cipher'].asOctets()[16:]))) # Key Usage 3 # AS-REP encrypted part (includes TGS session key or # application session key), encrypted with the client key # (Section 5.4.2) try: plainText = cipher.decrypt(key, 3, cipherText) except InvalidChecksum as e: # probably bad password if preauth is disabled if preAuth is False: error_msg = "failed to decrypt session key: %s" % str(e) raise SessionKeyDecryptionError(error_msg, asRep, cipher, key, cipherText) raise encASRepPart = decoder.decode(plainText, asn1Spec=EncASRepPart())[0] # Get the session key and the ticket cipher = _enctype_table[encASRepPart['key']['keytype']] sessionKey = Key(cipher.enctype, encASRepPart['key']['keyvalue'].asOctets()) # ToDo: Check Nonces! return tgt, cipher, key, sessionKey
# Yes preAuth = False encryptionTypesData = dict() salt = '' if preAuth is False: # In theory, we should have the right credentials for the etype specified before. methods = asRep['padata'] encryptionTypesData[supportedCiphers[0]] = salt # handle RC4 fallback, we don't need any salt tgt = r else: methods = decoder.decode(str(asRep['e-data']), asn1Spec=METHOD_DATA())[0] for method in methods: if method['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO2.value: etypes2 = decoder.decode(str(method['padata-value']), asn1Spec = ETYPE_INFO2())[0] for etype2 in etypes2: try: if etype2['salt'] is None or etype2['salt'].hasValue() is False: salt = '' else: salt = str(etype2['salt']) except PyAsn1Error, e: salt = '' encryptionTypesData[etype2['etype']] = salt elif method['padata-type'] == constants.PreAuthenticationDataTypes.PA_ETYPE_INFO.value: etypes = decoder.decode(str(method['padata-value']), asn1Spec = ETYPE_INFO())[0] for etype in etypes: try: if etype['salt'] is None or etype['salt'].hasValue() is False: