def to_asn1(self): krbcredinfo = {} krbcredinfo['key'] = EncryptionKey({ 'keytype': self.KeyType, 'keyvalue': self.Key }) krbcredinfo['prealm'] = self.AltTargetDomainName krbcredinfo['pname'] = PrincipalName({ 'name-type': self.EClientName_type, 'name-string': self.EClientName }) krbcredinfo['flags'] = core.IntegerBitString( self.TicketFlags).cast(TicketFlags) krbcredinfo['starttime'] = self.StartTime krbcredinfo['endtime'] = self.EndTime krbcredinfo['renew-till'] = self.RenewUntil krbcredinfo['srealm'] = self.DomainName krbcredinfo['sname'] = PrincipalName({ 'name-type': self.ServiceName_type, 'name-string': self.ServiceName }) enc_krbcred = {} enc_krbcred['ticket-info'] = [KrbCredInfo(krbcredinfo)] ticket = {} ticket['tkt-vno'] = krb5_pvno ticket['realm'] = self.DomainName ticket['sname'] = PrincipalName({ 'name-type': NAME_TYPE.SRV_INST.value, 'name-string': self.ServiceName }) ticket['enc-part'] = EncryptedData({ 'etype': self.TicketEncType, 'kvno': self.TicketKvno, 'cipher': self.Ticket }) krbcred = {} krbcred['pvno'] = krb5_pvno krbcred['msg-type'] = MESSAGE_TYPE.KRB_CRED.value krbcred['tickets'] = [Ticket(ticket)] krbcred['enc-part'] = EncryptedData({ 'etype': EncryptionType.NULL.value, 'cipher': EncKrbCredPart(enc_krbcred).dump() }) return KRBCRED(krbcred)
def construct_apreq_from_ticket(ticket_data, sessionkey, crealm, cname, flags = None, seq_number = 0, ap_opts = []): """ 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 = 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 construct_apreq(self, tgs, encTGSRepPart, sessionkey, flags = None, seq_number = 0, ap_opts = []): 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 = 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 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) krbcredinfo['starttime'] = datetime.datetime.fromtimestamp(self.time.starttime) krbcredinfo['endtime'] = datetime.datetime.fromtimestamp(self.time.endtime) 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) 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 tgt_to_kirbi(tgs, encTGSRepPart): #encTGSRepPart is already decrypted at this point ci = {} ci['key'] = encTGSRepPart['key'] ci['prealm'] = tgs['crealm'] ci['pname'] = tgs['cname'] ci['flags'] = encTGSRepPart['flags'] ci['authtime'] = encTGSRepPart['authtime'] ci['starttime'] = encTGSRepPart['starttime'] ci['endtime'] = encTGSRepPart['endtime'] ci['renew-till'] = encTGSRepPart['renew-till'] ci['srealm'] = encTGSRepPart['srealm'] ci['sname'] = encTGSRepPart['sname'] ti = {} ti['ticket-info'] = [KrbCredInfo(ci)] te = {} te['etype'] = 0 te['cipher'] = EncKrbCredPart(ti).dump() t = {} t['pvno'] = 5 t['msg-type'] = 22 t['enc-part'] = EncryptedData(te) t['tickets'] = [tgs['ticket']] return KRB_CRED(t)
def get_tgt(self, target=None): if target is None: logon = get_logon_info() if logon['logonserver'] is None: raise Exception( 'Failed to get logonserver and no target was specified! This wont work.' ) target = 'cifs/%s' % logon['logonserver'] ctx = AcquireCredentialsHandle(None, 'kerberos', target, SECPKG_CRED.OUTBOUND) res, ctx, data, outputflags, expiry = InitializeSecurityContext( ctx, target, token=None, ctx=ctx, flags=ISC_REQ.DELEGATE | ISC_REQ.MUTUAL_AUTH | ISC_REQ.ALLOCATE_MEMORY) if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED: #key_data = sspi._get_session_key() raw_ticket = self.export_ticketdata_target(0, target) key = Key(raw_ticket['Key']['KeyType'], raw_ticket['Key']['Key']) token = InitialContextToken.load(data[0][1]) ticket = AP_REQ(token.native['innerContextToken']).native cipher = _enctype_table[ticket['authenticator']['etype']] dec_authenticator = cipher.decrypt( key, 11, ticket['authenticator']['cipher']) authenticator = Authenticator.load(dec_authenticator).native if authenticator['cksum']['cksumtype'] != 0x8003: raise Exception('Checksum not good :(') checksum_data = AuthenticatorChecksum.from_bytes( authenticator['cksum']['checksum']) if ChecksumFlags.GSS_C_DELEG_FLAG not in checksum_data.flags: raise Exception('delegation flag not set!') cred_orig = KRB_CRED.load(checksum_data.delegation_data).native dec_authenticator = cipher.decrypt(key, 14, cred_orig['enc-part']['cipher']) #info = EncKrbCredPart.load(dec_authenticator).native #reconstructing kirbi with the unencrypted data te = {} te['etype'] = 0 te['cipher'] = dec_authenticator ten = EncryptedData(te) t = {} t['pvno'] = cred_orig['pvno'] t['msg-type'] = cred_orig['msg-type'] t['tickets'] = cred_orig['tickets'] t['enc-part'] = ten cred = KRB_CRED(t) return cred.dump()
def to_tgt(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 build_asreq_lts(self, supported_encryption_method, kdcopts = ['forwardable','renewable','proxiable']): logger.debug('Constructing TGT request with auth data') #now to create an AS_REQ with encrypted timestamp for authentication pa_data_1 = {} pa_data_1['padata-type'] = int(PADATA_TYPE('PA-PAC-REQUEST')) pa_data_1['padata-value'] = PA_PAC_REQUEST({'include-pac': True}).dump() now = datetime.datetime.now(datetime.timezone.utc) #creating timestamp asn1 timestamp = PA_ENC_TS_ENC({'patimestamp': now.replace(microsecond=0), 'pausec': now.microsecond}).dump() logger.debug('Selecting common encryption type: %s' % supported_encryption_method.name) self.kerberos_cipher = _enctype_table[supported_encryption_method.value] self.kerberos_cipher_type = supported_encryption_method.value self.kerberos_key = Key(self.kerberos_cipher.enctype, self.usercreds.get_key_for_enctype(supported_encryption_method, salt = self.server_salt)) enc_timestamp = self.kerberos_cipher.encrypt(self.kerberos_key, 1, timestamp, None) pa_data_2 = {} pa_data_2['padata-type'] = int(PADATA_TYPE('ENC-TIMESTAMP')) pa_data_2['padata-value'] = EncryptedData({'etype': supported_encryption_method.value, 'cipher': enc_timestamp}).dump() kdc_req_body = {} kdc_req_body['kdc-options'] = KDCOptions(set(kdcopts)) kdc_req_body['cname'] = PrincipalName({'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': [self.usercreds.username]}) kdc_req_body['realm'] = self.usercreds.domain.upper() kdc_req_body['sname'] = PrincipalName({'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': ['krbtgt', self.usercreds.domain.upper()]}) kdc_req_body['till'] = (now + datetime.timedelta(days=1)).replace(microsecond=0) kdc_req_body['rtime'] = (now + datetime.timedelta(days=1)).replace(microsecond=0) kdc_req_body['nonce'] = secrets.randbits(31) kdc_req_body['etype'] = [supported_encryption_method.value] #selecting according to server's preferences kdc_req = {} kdc_req['pvno'] = krb5_pvno kdc_req['msg-type'] = MESSAGE_TYPE.KRB_AS_REQ.value kdc_req['padata'] = [pa_data_2,pa_data_1] kdc_req['req-body'] = KDC_REQ_BODY(kdc_req_body) return AS_REQ(kdc_req)
async def authenticate(self, authData, flags=None, seq_number=0, is_rpc=False): try: if self.kc is None: _, err = await self.setup_kc() if err is not None: return None, None, err if self.iterations == 0: try: #check TGS first, maybe ccache already has what we need for target in self.ccred.ccache.list_targets(): # just printing this to debug... logger.debug('CCACHE target SPN record: %s' % target) tgs, encpart, self.session_key = await self.kc.get_TGS( self.spn) self.from_ccache = True except Exception as e: # this is normal when no credentials stored in ccache #tgt = await self.kc.get_TGT(override_etype=[18]) tgt = await self.kc.get_TGT() tgs, encpart, self.session_key = await self.kc.get_TGS( self.spn) ap_opts = [] if is_rpc == True: if self.iterations == 0: ap_opts.append('mutual-required') flags = ChecksumFlags.GSS_C_CONF_FLAG | ChecksumFlags.GSS_C_INTEG_FLAG | ChecksumFlags.GSS_C_SEQUENCE_FLAG|\ ChecksumFlags.GSS_C_REPLAY_FLAG | ChecksumFlags.GSS_C_MUTUAL_FLAG | ChecksumFlags.GSS_C_DCE_STYLE if self.from_ccache is False: apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags=flags, seq_number=seq_number, ap_opts=ap_opts) else: apreq = self.kc.construct_apreq_from_ticket( Ticket(tgs['ticket']).dump(), self.session_key, tgs['crealm'], tgs['cname']['name-string'][0], flags=flags, seq_number=seq_number, ap_opts=ap_opts, cb_data=None) self.iterations += 1 return apreq, False, None else: #mutual authentication part here self.seq_number = seq_number aprep = AP_REP.load(authData).native cipher = _enctype_table[int(aprep['enc-part']['etype'])]() cipher_text = aprep['enc-part']['cipher'] temp = cipher.decrypt(self.session_key, 12, cipher_text) enc_part = EncAPRepPart.load(temp).native cipher = _enctype_table[int( enc_part['subkey']['keytype'])]() now = datetime.datetime.now(datetime.timezone.utc) apreppart_data = {} apreppart_data['cusec'] = now.microsecond apreppart_data['ctime'] = now.replace(microsecond=0) apreppart_data['seq-number'] = enc_part['seq-number'] #print('seq %s' % enc_part['seq-number']) apreppart_data_enc = cipher.encrypt( self.session_key, 12, EncAPRepPart(apreppart_data).dump(), None) #overriding current session key self.session_key = Key(cipher.enctype, enc_part['subkey']['keyvalue']) ap_rep = {} ap_rep['pvno'] = 5 ap_rep['msg-type'] = MESSAGE_TYPE.KRB_AP_REP.value ap_rep['enc-part'] = EncryptedData({ 'etype': self.session_key.enctype, 'cipher': apreppart_data_enc }) token = AP_REP(ap_rep).dump() self.gssapi = get_gssapi(self.session_key) self.iterations += 1 return token, False, None else: if self.from_ccache is False: apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags=flags, seq_number=seq_number, ap_opts=ap_opts) else: apreq = self.kc.construct_apreq_from_ticket( Ticket(tgs['ticket']).dump(), self.session_key, tgs['crealm'], tgs['cname']['name-string'][0], flags=flags, seq_number=seq_number, ap_opts=ap_opts, cb_data=None) return apreq, False, None except Exception as e: return None, None, e
def do_preauth(self, rep): #now getting server's supported encryption methods supp_enc_methods = collections.OrderedDict() for enc_method in METHOD_DATA.load(rep['e-data']).native: data_type = PaDataType(enc_method['padata-type']) if data_type == PaDataType.ETYPE_INFO or data_type == PaDataType.ETYPE_INFO2: if data_type == PaDataType.ETYPE_INFO: enc_info_list = ETYPE_INFO.load(enc_method['padata-value']) elif data_type == PaDataType.ETYPE_INFO2: enc_info_list = ETYPE_INFO2.load(enc_method['padata-value']) for enc_info in enc_info_list.native: supp_enc_methods[EncryptionType(enc_info['etype'])] = enc_info['salt'] logger.debug('Server supports encryption type %s with salt %s' % (EncryptionType(enc_info['etype']).name, enc_info['salt'])) logger.debug('Constructing TGT request with auth data') #now to create an AS_REQ with encrypted timestamp for authentication pa_data_1 = {} pa_data_1['padata-type'] = int(PADATA_TYPE('PA-PAC-REQUEST')) pa_data_1['padata-value'] = PA_PAC_REQUEST({'include-pac': True}).dump() now = datetime.datetime.now(datetime.timezone.utc) #creating timestamp asn1 timestamp = PA_ENC_TS_ENC({'patimestamp': now.replace(microsecond=0), 'pausec': now.microsecond}).dump() supp_enc = self.usercreds.get_preferred_enctype(supp_enc_methods) logger.debug('Selecting common encryption type: %s' % supp_enc.name) self.kerberos_cipher = _enctype_table[supp_enc.value] self.kerberos_cipher_type = supp_enc.value if 'salt' in enc_info and enc_info['salt'] is not None: self.server_salt = enc_info['salt'].encode() self.kerberos_key = Key(self.kerberos_cipher.enctype, self.usercreds.get_key_for_enctype(supp_enc, salt = self.server_salt)) enc_timestamp = self.kerberos_cipher.encrypt(self.kerberos_key, 1, timestamp, None) pa_data_2 = {} pa_data_2['padata-type'] = int(PADATA_TYPE('ENC-TIMESTAMP')) pa_data_2['padata-value'] = EncryptedData({'etype': supp_enc.value, 'cipher': enc_timestamp}).dump() kdc_req_body = {} kdc_req_body['kdc-options'] = KDCOptions(set(['forwardable','renewable','proxiable'])) kdc_req_body['cname'] = PrincipalName({'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': [self.usercreds.username]}) kdc_req_body['realm'] = self.usercreds.domain.upper() kdc_req_body['sname'] = PrincipalName({'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': ['krbtgt', self.usercreds.domain.upper()]}) kdc_req_body['till'] = (now + datetime.timedelta(days=1)).replace(microsecond=0) kdc_req_body['rtime'] = (now + datetime.timedelta(days=1)).replace(microsecond=0) kdc_req_body['nonce'] = secrets.randbits(31) kdc_req_body['etype'] = [supp_enc.value] kdc_req = {} kdc_req['pvno'] = krb5_pvno kdc_req['msg-type'] = MESSAGE_TYPE.KRB_AS_REQ.value kdc_req['padata'] = [pa_data_2,pa_data_1] kdc_req['req-body'] = KDC_REQ_BODY(kdc_req_body) req = AS_REQ(kdc_req) logger.debug('Sending TGT request to server') return self.ksoc.sendrecv(req.dump())
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) 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) authenticator_data['seq-number'] = 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_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
def build_apreq(self, asrep, session_key, cipher, subkey_data, krb_finished_data, flags=GSSAPIFlags.GSS_C_MUTUAL_FLAG | GSSAPIFlags.GSS_C_INTEG_FLAG | GSSAPIFlags.GSS_C_EXTENDED_ERROR_FLAG): # TODO: https://www.ietf.org/rfc/rfc4757.txt #subkey_data = {} #subkey_data['keytype'] = Enctype.AES256 #subkey_data['keyvalue'] = os.urandom(32) subkey_cipher = _enctype_table[subkey_data['keytype']] subkey_key = Key(subkey_cipher.enctype, subkey_data['keyvalue']) subkey_checksum = _checksum_table[ 16] # ChecksumTypes.hmac_sha1_96_aes256 krb_finished_checksum_data = {} krb_finished_checksum_data['cksumtype'] = 16 krb_finished_checksum_data['checksum'] = subkey_checksum.checksum( subkey_key, 41, krb_finished_data) krb_finished_data = {} krb_finished_data['gss-mic'] = Checksum(krb_finished_checksum_data) krb_finished = KRB_FINISHED(krb_finished_data).dump() a = 2 extensions_data = a.to_bytes( 4, byteorder='big', signed=True) + len(krb_finished).to_bytes( 4, byteorder='big', signed=True) + krb_finished ac = AuthenticatorChecksum() ac.flags = flags ac.channel_binding = b'\x00' * 16 chksum = {} chksum['cksumtype'] = 0x8003 chksum['checksum'] = ac.to_bytes() + extensions_data tii = LSAP_TOKEN_INFO_INTEGRITY() tii.Flags = 1 tii.TokenIL = 0x00002000 # Medium integrity tii.MachineID = bytes.fromhex( '7e303fffe6bff25146addca4fbddf1b94f1634178eb4528fb2731c669ca23cde') restriction_data = {} restriction_data['restriction-type'] = 0 restriction_data['restriction'] = tii.to_bytes() restriction_data = KERB_AD_RESTRICTION_ENTRY(restriction_data) x = KERB_AD_RESTRICTION_ENTRYS([restriction_data]).dump() restrictions = AuthorizationData([{ 'ad-type': 141, 'ad-data': x }]).dump() now = datetime.datetime.now(datetime.timezone.utc) authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm(asrep['crealm']) authenticator_data['cname'] = asrep['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now.replace(microsecond=0) authenticator_data['subkey'] = EncryptionKey(subkey_data) authenticator_data['seq-number'] = 682437742 #??? TODO: check this! authenticator_data['authorization-data'] = AuthorizationData([{ 'ad-type': 1, 'ad-data': restrictions }]) authenticator_data['cksum'] = Checksum(chksum) #print('Authenticator(authenticator_data).dump()') #print(Authenticator(authenticator_data).dump().hex()) authenticator_data_enc = cipher.encrypt( session_key, 11, Authenticator(authenticator_data).dump(), None) ap_opts = ['mutual-required'] ap_req = {} ap_req['pvno'] = krb5_pvno ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value ap_req['ticket'] = Ticket(asrep['ticket']) ap_req['ap-options'] = APOptions(set(ap_opts)) ap_req['authenticator'] = EncryptedData({ 'etype': session_key.enctype, 'cipher': authenticator_data_enc }) #pprint('AP_REQ \r\n%s' % AP_REQ(ap_req).native) #print(AP_REQ(ap_req).dump().hex()) #input() return AP_REQ(ap_req).dump()
async def authenticate(self, authData, flags=None, seq_number=0, is_rpc=False): if self.iterations == 0: #tgt = await self.kc.get_TGT(override_etype=[18]) tgt = await self.kc.get_TGT() tgs, encpart, self.session_key = await self.kc.get_TGS(self.spn) ap_opts = [] if is_rpc == True: if self.iterations == 0: ap_opts.append('mutual-required') flags = ChecksumFlags.GSS_C_CONF_FLAG | ChecksumFlags.GSS_C_INTEG_FLAG | ChecksumFlags.GSS_C_SEQUENCE_FLAG|\ ChecksumFlags.GSS_C_REPLAY_FLAG | ChecksumFlags.GSS_C_MUTUAL_FLAG | ChecksumFlags.GSS_C_DCE_STYLE apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags=flags, seq_number=seq_number, ap_opts=ap_opts) self.iterations += 1 return apreq, False else: #mutual authentication part here aprep = AP_REP.load(authData).native cipher = _enctype_table[int(aprep['enc-part']['etype'])]() cipher_text = aprep['enc-part']['cipher'] temp = cipher.decrypt(self.session_key, 12, cipher_text) enc_part = EncAPRepPart.load(temp).native cipher = _enctype_table[int(enc_part['subkey']['keytype'])]() now = datetime.datetime.now(datetime.timezone.utc) apreppart_data = {} apreppart_data['cusec'] = now.microsecond apreppart_data['ctime'] = now.replace(microsecond=0) apreppart_data['seq-number'] = enc_part['seq-number'] apreppart_data_enc = cipher.encrypt( self.session_key, 12, EncAPRepPart(apreppart_data).dump(), None) #overriding current session key self.session_key = Key(cipher.enctype, enc_part['subkey']['keyvalue']) ap_rep = {} ap_rep['pvno'] = 5 ap_rep['msg-type'] = MESSAGE_TYPE.KRB_AP_REP.value ap_rep['enc-part'] = EncryptedData({ 'etype': self.session_key.enctype, 'cipher': apreppart_data_enc }) token = AP_REP(ap_rep).dump() self.gssapi = get_gssapi(self.session_key) self.iterations += 1 return token, False else: apreq = self.kc.construct_apreq(tgs, encpart, self.session_key, flags=flags, seq_number=seq_number, ap_opts=ap_opts) return apreq, False
async 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 ]): 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','forwarded','renewable','renewable-ok', 'canonicalize'])) 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) reply = await self.ksoc.sendrecv(req.dump()) if reply.name == 'KRB_ERROR': if reply.native['error-code'] == 16: logger.error( 'S4U2proxy: Failed to get S4U2proxy! Error code (16) indicates that delegation is not enabled for this account!' ) raise Exception('S4U2proxy failed! %s' % str(reply))