def add_tgs(self, tgs_rep, enc_tgs_rep_part, override_pp=False): #from AS_REP """ Creates credential object from the TGS and adds to the ccache file The TGS is the native representation of the asn1 encoded TGS_REP data when the user requests a tgs to a specific service principal with a valid TGT This function doesn't do decryption of the encrypted part of the tgs_rep object, it is expected that the decrypted XXX is supplied in enc_as_rep_part override_pp: bool to determine if client principal should be used as the primary principal for the ccache file """ c = Credential() c.client = CCACHEPrincipal.from_asn1(tgs_rep['cname'], tgs_rep['crealm']) if override_pp == True: self.primary_principal = c.client c.server = CCACHEPrincipal.from_asn1(enc_tgs_rep_part['sname'], enc_tgs_rep_part['srealm']) c.time = Times.from_asn1(enc_tgs_rep_part) c.key = Keyblock.from_asn1(enc_tgs_rep_part['key']) c.is_skey = 0 #not sure! c.tktflags = TicketFlags(enc_tgs_rep_part['flags']).cast( core.IntegerBitString).native c.num_address = 0 c.num_authdata = 0 c.ticket = CCACHEOctetString.from_asn1( Ticket(tgs_rep['ticket']).dump()) c.second_ticket = CCACHEOctetString.empty() self.credentials.append(c)
def construct_apreq_from_ticket(ticket_data, sessionkey, crealm, cname, flags=None, seq_number=0, ap_opts=[], cb_data=None): """ ticket: bytes of Ticket """ now = datetime.datetime.now(datetime.timezone.utc) authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm(crealm) authenticator_data['cname'] = PrincipalName({ 'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': [cname] }) authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now.replace(microsecond=0) if flags is not None: ac = AuthenticatorChecksum() ac.flags = flags ac.channel_binding = cb_data if cb_data is None: ac.channel_binding = b'\x00' * 16 chksum = {} chksum['cksumtype'] = 0x8003 chksum['checksum'] = ac.to_bytes() authenticator_data['cksum'] = Checksum(chksum) authenticator_data['seq-number'] = seq_number cipher = _enctype_table[sessionkey.enctype] authenticator_data_enc = cipher.encrypt( sessionkey, 11, Authenticator(authenticator_data).dump(), None) ap_req = {} ap_req['pvno'] = krb5_pvno ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value ap_req['ticket'] = Ticket.load(ticket_data) ap_req['ap-options'] = APOptions(set(ap_opts)) ap_req['authenticator'] = EncryptedData({ 'etype': sessionkey.enctype, 'cipher': authenticator_data_enc }) return AP_REQ(ap_req).dump()
def get_hashes(self, all_hashes=False): """ Returns a list of hashes in hashcat-firendly format for tickets with encryption type 23 (which is RC4) all_hashes: overrides the encryption type filtering and returns hash for all tickets """ hashes = [] for cred in self.credentials: res = Ticket.load(cred.ticket.to_asn1()).native if int(res['enc-part']['etype']) == 23 or all_hashes == True: hashes.append(cred.to_hash()) return hashes
def construct_apreq(self, tgs, encTGSRepPart, sessionkey, flags=None, seq_number=0, ap_opts=[], cb_data=None): now = datetime.datetime.now(datetime.timezone.utc) authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm(self.kerberos_TGT['crealm']) authenticator_data['cname'] = self.kerberos_TGT['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now.replace(microsecond=0) if flags is not None: ac = AuthenticatorChecksum() ac.flags = flags ac.channel_binding = cb_data if cb_data is None: ac.channel_binding = b'\x00' * 16 chksum = {} chksum['cksumtype'] = 0x8003 chksum['checksum'] = ac.to_bytes() authenticator_data['cksum'] = Checksum(chksum) authenticator_data['seq-number'] = seq_number cipher = _enctype_table[encTGSRepPart['key']['keytype']] authenticator_data_enc = cipher.encrypt( sessionkey, 11, Authenticator(authenticator_data).dump(), None) ap_req = {} ap_req['pvno'] = krb5_pvno ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value ap_req['ticket'] = Ticket(tgs['ticket']) ap_req['ap-options'] = APOptions(set(ap_opts)) ap_req['authenticator'] = EncryptedData({ 'etype': self.kerberos_cipher_type, 'cipher': authenticator_data_enc }) return AP_REQ(ap_req).dump()
def add_kirbi(self, krbcred, override_pp=True, include_expired=False): c = Credential() enc_credinfo = EncKrbCredPart.load( krbcred['enc-part']['cipher']).native ticket_info = enc_credinfo['ticket-info'][0] """ if ticket_info['endtime'] < datetime.datetime.now(datetime.timezone.utc): if include_expired == True: logging.debug('This ticket has most likely expired, but include_expired is forcing me to add it to cache! This can cause problems!') else: logging.debug('This ticket has most likely expired, skipping') return """ c.client = CCACHEPrincipal.from_asn1(ticket_info['pname'], ticket_info['prealm']) if override_pp == True: self.primary_principal = c.client #yaaaaay 4 additional weirdness!!!! #if sname name-string contains a realm as well htne impacket will crash miserably :( if len(ticket_info['sname']['name-string'] ) > 2 and ticket_info['sname']['name-string'][-1].upper( ) == ticket_info['srealm'].upper(): logger.debug('SNAME contains the realm as well, trimming it') t = ticket_info['sname'] t['name-string'] = t['name-string'][:-1] c.server = CCACHEPrincipal.from_asn1(t, ticket_info['srealm']) else: c.server = CCACHEPrincipal.from_asn1(ticket_info['sname'], ticket_info['srealm']) c.time = Times.from_asn1(ticket_info) c.key = Keyblock.from_asn1(ticket_info['key']) c.is_skey = 0 #not sure! c.tktflags = TicketFlags(ticket_info['flags']).cast( core.IntegerBitString).native c.num_address = 0 c.num_authdata = 0 c.ticket = CCACHEOctetString.from_asn1( Ticket(krbcred['tickets'] [0]).dump()) #kirbi only stores one ticket per file c.second_ticket = CCACHEOctetString.empty() self.credentials.append(c)
def to_tgs(self): """ Returns the native format of an AS_REP message and the sessionkey in EncryptionKey native format """ enc_part = EncryptedData({'etype': 1, 'cipher': b''}) tgt_rep = {} tgt_rep['pvno'] = krb5_pvno tgt_rep['msg-type'] = MESSAGE_TYPE.KRB_AS_REP.value tgt_rep['crealm'] = self.server.realm.to_string() tgt_rep['cname'] = self.client.to_asn1()[0] tgt_rep['ticket'] = Ticket.load(self.ticket.to_asn1()).native tgt_rep['enc-part'] = enc_part.native t = EncryptionKey(self.key.to_asn1()).native return tgt_rep, t
def to_kirbi(self): filename = '%s@%s_%s' % ( self.client.to_string(), self.server.to_string(), hashlib.sha1(self.ticket.to_asn1()).hexdigest()[:8]) krbcredinfo = {} krbcredinfo['key'] = EncryptionKey(self.key.to_asn1()) krbcredinfo['prealm'] = self.client.realm.to_string() krbcredinfo['pname'] = self.client.to_asn1()[0] krbcredinfo['flags'] = core.IntegerBitString( self.tktflags).cast(TicketFlags) if self.time.authtime != 0: #this parameter is not mandatory, and most of the time not present krbcredinfo['authtime'] = datetime.datetime.fromtimestamp( self.time.authtime, datetime.timezone.utc) if self.time.starttime != 0: krbcredinfo['starttime'] = datetime.datetime.fromtimestamp( self.time.starttime, datetime.timezone.utc) if self.time.endtime != 0: krbcredinfo['endtime'] = datetime.datetime.fromtimestamp( self.time.endtime, datetime.timezone.utc) if self.time.renew_till != 0: #this parameter is not mandatory, and sometimes it's not present krbcredinfo['renew-till'] = datetime.datetime.fromtimestamp( self.time.authtime, datetime.timezone.utc) krbcredinfo['srealm'] = self.server.realm.to_string() krbcredinfo['sname'] = self.server.to_asn1()[0] enc_krbcred = {} enc_krbcred['ticket-info'] = [KrbCredInfo(krbcredinfo)] krbcred = {} krbcred['pvno'] = krb5_pvno krbcred['msg-type'] = MESSAGE_TYPE.KRB_CRED.value krbcred['tickets'] = [Ticket.load(self.ticket.to_asn1())] krbcred['enc-part'] = EncryptedData({ 'etype': EncryptionType.NULL.value, 'cipher': EncKrbCredPart(enc_krbcred).dump() }) kirbi = KRBCRED(krbcred) return kirbi, filename
def to_hash(self): res = Ticket.load(self.ticket.to_asn1()).native tgs_encryption_type = int(res['enc-part']['etype']) t = len(res['sname']['name-string']) if t == 1: tgs_name_string = res['sname']['name-string'][0] else: tgs_name_string = res['sname']['name-string'][1] tgs_realm = res['realm'] if tgs_encryption_type == EncryptionType.AES256_CTS_HMAC_SHA1_96.value: tgs_checksum = res['enc-part']['cipher'][-12:] tgs_encrypted_data2 = res['enc-part']['cipher'][:-12] return '$krb5tgs$%s$%s$%s$%s$%s' % ( tgs_encryption_type, tgs_name_string, tgs_realm, tgs_checksum.hex(), tgs_encrypted_data2.hex()) else: tgs_checksum = res['enc-part']['cipher'][:16] tgs_encrypted_data2 = res['enc-part']['cipher'][16:] return '$krb5tgs$%s$*%s$%s$spn*$%s$%s' % ( tgs_encryption_type, tgs_name_string, tgs_realm, tgs_checksum.hex(), tgs_encrypted_data2.hex())
def S4U2proxy(self, s4uself_ticket, spn_user, supp_enc_methods=[ EncryptionType.DES_CBC_CRC, EncryptionType.DES_CBC_MD4, EncryptionType.DES_CBC_MD5, EncryptionType.DES3_CBC_SHA1, EncryptionType.ARCFOUR_HMAC_MD5, EncryptionType.AES256_CTS_HMAC_SHA1_96, EncryptionType.AES128_CTS_HMAC_SHA1_96 ]): logger.debug('[S4U2proxy] Impersonating %s' % '/'.join(spn_user.get_principalname())) now = datetime.datetime.now(datetime.timezone.utc) supp_enc = self.usercreds.get_preferred_enctype(supp_enc_methods) pa_pac_opts = {} pa_pac_opts['padata-type'] = int(PADATA_TYPE('PA-PAC-OPTIONS')) pa_pac_opts['padata-value'] = PA_PAC_OPTIONS({ 'value': PA_PAC_OPTIONSTypes(set(['resource-based constrained delegation'])) }).dump() authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm(self.kerberos_TGT['crealm']) authenticator_data['cname'] = self.kerberos_TGT['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now.replace(microsecond=0) authenticator_data_enc = self.kerberos_cipher.encrypt( self.kerberos_session_key, 7, Authenticator(authenticator_data).dump(), None) ap_req = {} ap_req['pvno'] = krb5_pvno ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value ap_req['ap-options'] = APOptions(set()) ap_req['ticket'] = Ticket(self.kerberos_TGT['ticket']) ap_req['authenticator'] = EncryptedData({ 'etype': self.kerberos_cipher_type, 'cipher': authenticator_data_enc }) pa_tgs_req = {} pa_tgs_req['padata-type'] = PaDataType.TGS_REQ.value pa_tgs_req['padata-value'] = AP_REQ(ap_req).dump() krb_tgs_body = {} krb_tgs_body['kdc-options'] = KDCOptions( set([ 'forwardable', 'renewable', 'constrained-delegation', 'canonicalize' ])) krb_tgs_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.SRV_INST.value, 'name-string': spn_user.get_principalname() }) krb_tgs_body['realm'] = self.usercreds.domain.upper() krb_tgs_body['till'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) krb_tgs_body['nonce'] = secrets.randbits(31) krb_tgs_body['etype'] = [ supp_enc.value ] #selecting according to server's preferences krb_tgs_body['additional-tickets'] = [s4uself_ticket] krb_tgs_req = {} krb_tgs_req['pvno'] = krb5_pvno krb_tgs_req['msg-type'] = MESSAGE_TYPE.KRB_TGS_REQ.value krb_tgs_req['padata'] = [pa_tgs_req, pa_pac_opts] krb_tgs_req['req-body'] = KDC_REQ_BODY(krb_tgs_body) req = TGS_REQ(krb_tgs_req) logger.debug('[S4U2proxy] Sending request to server') try: reply = self.ksoc.sendrecv(req.dump()) except KerberosError as e: if e.errorcode.value == 16: logger.error( 'S4U2proxy: Failed to get S4U2proxy! Error code (16) indicates that delegation is not enabled for this account! Full error: %s' % e) raise e logger.debug('[S4U2proxy] Got server reply, decrypting...') tgs = reply.native encTGSRepPart = EncTGSRepPart.load( self.kerberos_cipher.decrypt(self.kerberos_session_key, 8, tgs['enc-part']['cipher'])).native key = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) self.ccache.add_tgs(tgs, encTGSRepPart) logger.debug('[S4U2proxy] Got valid TGS reply') return tgs, encTGSRepPart, key
def S4U2self(self, user_to_impersonate, supp_enc_methods=[ EncryptionType.DES_CBC_CRC, EncryptionType.DES_CBC_MD4, EncryptionType.DES_CBC_MD5, EncryptionType.DES3_CBC_SHA1, EncryptionType.ARCFOUR_HMAC_MD5, EncryptionType.AES256_CTS_HMAC_SHA1_96, EncryptionType.AES128_CTS_HMAC_SHA1_96 ]): """ user_to_impersonate : KerberosTarget class """ if not self.kerberos_TGT: logger.debug('[S4U2self] TGT is not available! Fetching TGT...') self.get_TGT() supp_enc = self.usercreds.get_preferred_enctype(supp_enc_methods) auth_package_name = 'Kerberos' now = datetime.datetime.now(datetime.timezone.utc) ###### Calculating authenticator data authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm(self.kerberos_TGT['crealm']) authenticator_data['cname'] = self.kerberos_TGT['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now.replace(microsecond=0) authenticator_data_enc = self.kerberos_cipher.encrypt( self.kerberos_session_key, 7, Authenticator(authenticator_data).dump(), None) ap_req = {} ap_req['pvno'] = krb5_pvno ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value ap_req['ap-options'] = APOptions(set()) ap_req['ticket'] = Ticket(self.kerberos_TGT['ticket']) ap_req['authenticator'] = EncryptedData({ 'etype': self.kerberos_cipher_type, 'cipher': authenticator_data_enc }) pa_data_auth = {} pa_data_auth['padata-type'] = PaDataType.TGS_REQ.value pa_data_auth['padata-value'] = AP_REQ(ap_req).dump() ###### Calculating checksum data S4UByteArray = NAME_TYPE.PRINCIPAL.value.to_bytes(4, 'little', signed=False) S4UByteArray += user_to_impersonate.username.encode() S4UByteArray += user_to_impersonate.domain.encode() S4UByteArray += auth_package_name.encode() logger.debug('[S4U2self] S4UByteArray: %s' % S4UByteArray.hex()) logger.debug('[S4U2self] S4UByteArray: %s' % S4UByteArray) chksum_data = _HMACMD5.checksum(self.kerberos_session_key, 17, S4UByteArray) logger.debug('[S4U2self] chksum_data: %s' % chksum_data.hex()) chksum = {} chksum['cksumtype'] = int(CKSUMTYPE('HMAC_MD5')) chksum['checksum'] = chksum_data ###### Filling out PA-FOR-USER data for impersonation pa_for_user_enc = {} pa_for_user_enc['userName'] = PrincipalName({ 'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': user_to_impersonate.get_principalname() }) pa_for_user_enc['userRealm'] = user_to_impersonate.domain pa_for_user_enc['cksum'] = Checksum(chksum) pa_for_user_enc['auth-package'] = auth_package_name pa_for_user = {} pa_for_user['padata-type'] = int(PADATA_TYPE('PA-FOR-USER')) pa_for_user['padata-value'] = PA_FOR_USER_ENC(pa_for_user_enc).dump() ###### Constructing body krb_tgs_body = {} krb_tgs_body['kdc-options'] = KDCOptions( set(['forwardable', 'renewable', 'canonicalize'])) krb_tgs_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.UNKNOWN.value, 'name-string': [self.usercreds.username] }) krb_tgs_body['realm'] = self.usercreds.domain.upper() krb_tgs_body['till'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) krb_tgs_body['nonce'] = secrets.randbits(31) krb_tgs_body['etype'] = [ supp_enc.value ] #selecting according to server's preferences krb_tgs_req = {} krb_tgs_req['pvno'] = krb5_pvno krb_tgs_req['msg-type'] = MESSAGE_TYPE.KRB_TGS_REQ.value krb_tgs_req['padata'] = [pa_data_auth, pa_for_user] krb_tgs_req['req-body'] = KDC_REQ_BODY(krb_tgs_body) req = TGS_REQ(krb_tgs_req) logger.debug('[S4U2self] Sending request to server') try: reply = self.ksoc.sendrecv(req.dump()) except KerberosError as e: if e.errorcode.value == 16: logger.error( '[S4U2self] Failed to get S4U2self! Error code (16) indicates that delegation is not enabled for this account! Full error: %s' % e) raise e logger.debug('[S4U2self] Got reply, decrypting...') tgs = reply.native encTGSRepPart = EncTGSRepPart.load( self.kerberos_cipher.decrypt(self.kerberos_session_key, 8, tgs['enc-part']['cipher'])).native key = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) self.ccache.add_tgs(tgs, encTGSRepPart) logger.debug('[S4U2self] Got valid TGS reply') self.kerberos_TGS = tgs return tgs, encTGSRepPart, key
def get_TGS(self, spn_user, override_etype=None, is_linux=False): """ Requests a TGS ticket for the specified user. Returns the TGS ticket, end the decrpyted encTGSRepPart. spn_user: KerberosTarget: the service user you want to get TGS for. override_etype: None or list of etype values (int) Used mostly for kerberoasting, will override the AP_REQ supported etype values (which is derived from the TGT) to be able to recieve whatever tgs tiecket """ logger.debug('[getTGS] Constructing request for user %s' % spn_user.get_formatted_pname()) now = datetime.datetime.now(datetime.timezone.utc) kdc_req_body = {} kdc_req_body['kdc-options'] = KDCOptions( set(['forwardable', 'renewable', 'renewable_ok', 'canonicalize'])) kdc_req_body['realm'] = spn_user.domain.upper() kdc_req_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.SRV_INST.value, 'name-string': spn_user.get_principalname() }) kdc_req_body['till'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) kdc_req_body['nonce'] = secrets.randbits(31) if override_etype: kdc_req_body['etype'] = override_etype else: kdc_req_body['etype'] = [self.kerberos_cipher_type] authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm(self.kerberos_TGT['crealm']) authenticator_data['cname'] = self.kerberos_TGT['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now.replace(microsecond=0) authenticator_data['seq-number'] = 0 if is_linux: ac = AuthenticatorChecksum() ac.flags = 0 ac.channel_binding = b'\x00' * 16 chksum = {} chksum['cksumtype'] = 0x8003 chksum['checksum'] = ac.to_bytes() authenticator_data['cksum'] = Checksum(chksum) else: from pycquery_krb.protocol.encryption import Enctype, Cksumtype, make_checksum if Enctype.AES256 == self.kerberos_session_key.enctype: authenticator_data['cksum'] = Checksum({ 'cksumtype': Cksumtype.SHA1_AES256, 'checksum': make_checksum(Cksumtype.SHA1_AES256, self.kerberos_session_key, 6, KDC_REQ_BODY(kdc_req_body).dump()) }) authenticator_data_enc = self.kerberos_cipher.encrypt( self.kerberos_session_key, 7, Authenticator(authenticator_data).dump(), None) ap_req = {} ap_req['pvno'] = krb5_pvno ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value ap_req['ap-options'] = APOptions(set()) ap_req['ticket'] = Ticket(self.kerberos_TGT['ticket']) ap_req['authenticator'] = EncryptedData({ 'etype': self.kerberos_cipher_type, 'cipher': authenticator_data_enc }) pa_data_1 = {} pa_data_1['padata-type'] = PaDataType.TGS_REQ.value pa_data_1['padata-value'] = AP_REQ(ap_req).dump() kdc_req = {} kdc_req['pvno'] = krb5_pvno kdc_req['msg-type'] = MESSAGE_TYPE.KRB_TGS_REQ.value kdc_req['padata'] = [pa_data_1] kdc_req['req-body'] = KDC_REQ_BODY(kdc_req_body) req = TGS_REQ(kdc_req) logger.debug('[getTGS] Constructing request to server') rep = self.ksoc.sendrecv(req.dump()) logger.debug('[getTGS] Got reply, decrypting...') tgs = rep.native encTGSRepPart = EncTGSRepPart.load( self.kerberos_cipher.decrypt(self.kerberos_session_key, 8, tgs['enc-part']['cipher'])).native key = Key(encTGSRepPart['key']['keytype'], encTGSRepPart['key']['keyvalue']) self.ccache.add_tgs(tgs, encTGSRepPart) logger.debug('[getTGS] Got valid reply') self.kerberos_TGS = tgs return tgs, encTGSRepPart, key