async def authenticate(self, authData=None, flags=None, seq_number=0, is_rpc=False): #authdata is only for api compatibility reasons if is_rpc == True: if self.iterations == 0: flags = ISC_REQ.CONFIDENTIALITY | \ ISC_REQ.INTEGRITY | \ ISC_REQ.MUTUAL_AUTH | \ ISC_REQ.REPLAY_DETECT | \ ISC_REQ.SEQUENCE_DETECT|\ ISC_REQ.USE_DCE_STYLE token = self.ksspi.get_ticket_for_spn(self.target, flags=flags, is_rpc=True, token_data=authData) print(token.hex()) self.iterations += 1 return token, True elif self.iterations == 1: flags = ISC_REQ.USE_DCE_STYLE token = self.ksspi.get_ticket_for_spn(self.target, flags=flags, is_rpc=True, token_data=authData) print(token.hex()) aprep = AP_REP.load(token).native subkey = Key(aprep['enc-part']['etype'], self.get_session_key()) cipher_text = aprep['enc-part']['cipher'] cipher = _enctype_table[aprep['enc-part']['etype']]() plaintext = cipher.decrypt(subkey, 12, cipher_text) print('plaintext') input(plaintext.hex()) self.gssapi = get_gssapi(subkey) input(self.gssapi) self.iterations += 1 return token, False else: raise Exception('???????') else: apreq = self.ksspi.get_ticket_for_spn(self.target) return apreq, False
def from_tgt(ksoc, tgt, key): """ Sets up the kerberos object from tgt and the session key. Use this function when pulling the TGT from ccache file. """ kc = KerbrosComm(None, ksoc) kc.kerberos_TGT = tgt kc.kerberos_cipher_type = key['keytype'] kc.kerberos_session_key = Key(kc.kerberos_cipher_type, key['keyvalue']) kc.kerberos_cipher = _enctype_table[kc.kerberos_cipher_type] return kc
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.target) 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.utcnow() apreppart_data = {} apreppart_data['cusec'] = now.microsecond apreppart_data['ctime'] = now 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
def S4U2self(self, user_to_impersonate, supp_enc_methods=SUPPORTED_METHODS): """ user_to_impersonate : KerberosTarget class """ if not self.kerberos_TGT: logger.debug( 'S4U2self invoked, but 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.utcnow() # Calculating authenticator data authenticator_data = {} authenticator_data['authenticator-vno'] = krb5_pvno authenticator_data['crealm'] = Realm( as_str(self.kerberos_TGT['crealm'])) authenticator_data['cname'] = self.kerberos_TGT['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now 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' % as_hex(S4UByteArray)) logger.debug('S4U2self: S4UByteArray: %s' % S4UByteArray) chksum_data = _HMACMD5.checksum(self.kerberos_session_key, 17, S4UByteArray) logger.debug('S4U2self: chksum_data: %s' % as_hex(chksum_data)) 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) krb_tgs_body['nonce'], = struct.unpack('>i', os.urandom(4)) # selecting according to server's preferences krb_tgs_body['etype'] = [supp_enc.value] 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('Sending S4U2self 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 logger.debug('Got S4U2self 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('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. Retruns 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 """ # construct tgs_req logger.debug('Constructing TGS request for user %s' % spn_user.get_formatted_pname()) now = datetime.datetime.utcnow() kdc_req_body = {} kdc_req_body['kdc-options'] = KDCOptions( set(['forwardable', 'renewable', 'renewable_ok', 'canonicalize'])) kdc_req_body['realm'] = as_str(spn_user.domain.upper()) logger.debug('TGS principal: %s', spn_user.get_principalname()) kdc_req_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.SRV_INST.value, 'name-string': as_str(spn_user.get_principalname()) }) kdc_req_body['till'] = now + datetime.timedelta(days=1) kdc_req_body['nonce'], = struct.unpack('>i', os.urandom(4)) 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( as_str(self.kerberos_TGT['crealm'])) authenticator_data['cname'] = self.kerberos_TGT['cname'] authenticator_data['cusec'] = now.microsecond authenticator_data['ctime'] = now if is_linux: ac = AuthenticatorChecksum() ac.flags = 0 ac.channel_binding = b'\x00' * 16 chksum = {} chksum['cksumtype'] = 0x8003 chksum['checksum'] = ac.to_bytes() print(chksum['checksum']) 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('Constructing TGS request to server') rep = self.ksoc.sendrecv(req.dump()) logger.debug('Got TGS 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('Got valid TGS reply') self.kerberos_TGS = tgs return tgs, encTGSRepPart, key
def get_TGT(self, override_etype=None, decrypt_tgt=True): """ decrypt_tgt: used for asreproast attacks Steps performed: 1. Send and empty (no encrypted timestamp) AS_REQ with all the encryption types we support 2. Depending on the response (either error or AS_REP with TGT) we either send another AS_REQ with the encrypted data or return the TGT (or fail miserably) 3. PROFIT """ logger.debug('Generating initial TGT without authentication data') now = datetime.datetime.utcnow() 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': [as_str(self.usercreds.username)] }) kdc_req_body['realm'] = as_str(self.usercreds.domain.upper()) kdc_req_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': ['krbtgt', as_str(self.usercreds.domain).upper()] }) kdc_req_body['till'] = now + datetime.timedelta(days=1) kdc_req_body['rtime'] = now + datetime.timedelta(days=1) kdc_req_body['nonce'], = struct.unpack('>i', os.urandom(4)) if override_etype is None: kdc_req_body['etype'] = self.usercreds.get_supported_enctypes() else: kdc_req_body['etype'] = override_etype 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() kdc_req = {} kdc_req['pvno'] = krb5_pvno kdc_req['msg-type'] = MESSAGE_TYPE.KRB_AS_REQ.value kdc_req['padata'] = [pa_data_1] kdc_req['req-body'] = KDC_REQ_BODY(kdc_req_body) req = AS_REQ(kdc_req) logger.debug('Sending initial TGT to %s' % self.ksoc.get_addr_str()) rep = self.ksoc.sendrecv(req.dump(), throw=False) if rep.name != 'KRB_ERROR': # user can do kerberos auth without preauthentication! self.kerberos_TGT = rep.native # if we want to roast the asrep (tgt rep) part then we dont even have the proper keys to decrypt # so we just return, the asrep can be extracted from this object anyhow if decrypt_tgt == False: return self.kerberos_cipher = _get_enctype_profile[23] self.kerberos_cipher_type = 23 self.kerberos_key = Key( self.kerberos_cipher.enctype, self.usercreds.get_key_for_enctype( EncryptionType.ARCFOUR_HMAC_MD5)) else: if rep.native[ 'error-code'] != KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED.value: raise KerberosError(rep) rep = rep.native logger.debug('Got reply from server, asikg to provide auth data') rep = self.do_preauth(rep) logger.debug('Got valid TGT response from server') rep = rep.native self.kerberos_TGT = rep cipherText = rep['enc-part']['cipher'] temp = self.kerberos_cipher.decrypt(self.kerberos_key, 3, cipherText) try: self.kerberos_TGT_encpart = EncASRepPart.load(temp).native except Exception as e: logger.debug('EncAsRepPart load failed, is this linux?') try: self.kerberos_TGT_encpart = EncTGSRepPart.load(temp).native except Exception as e: logger.error('Failed to load decrypted part of the reply!') raise self.kerberos_session_key = Key( self.kerberos_cipher.enctype, self.kerberos_TGT_encpart['key']['keyvalue']) self.ccache.add_tgt(self.kerberos_TGT, self.kerberos_TGT_encpart, override_pp=True) logger.debug('Got valid TGT') return
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.utcnow() # creating timestamp asn1 timestamp = PA_ENC_TS_ENC({ 'patimestamp': now, '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 = _get_enctype_profile(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': [as_str(self.usercreds.username)] }) kdc_req_body['realm'] = as_str(self.usercreds.domain.upper()) kdc_req_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': ['krbtgt', as_str(self.usercreds.domain.upper())] }) kdc_req_body['till'] = now + datetime.timedelta(days=1) kdc_req_body['rtime'] = now + datetime.timedelta(days=1) kdc_req_body['nonce'], = struct.unpack('>i', os.urandom(4)) # selecting according to server's preferences 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())
logging.debug('Getting TGT') kc = KerbrosComm(ccred, ksoc) kc.get_TGT() #kc.ccache.to_file(args.ccache) logging.info('Done!') if args.verbose > 1: pprint.pprint(kc.kerberos_TGT) pprint.pprint(kc.kerberos_TGT_encpart) krbtgt_data = kc.kerberos_TGT['ticket']['enc-part']['cipher'] et = EncryptionType(kc.kerberos_TGT['ticket']['enc-part']['etype']) krbtgt_key = Key(kc.kerberos_TGT['ticket']['enc-part']['etype'], ccred_krbtgt.get_key_for_enctype(et)) krbtgt_cipher = _enctype_table[kc.kerberos_TGT['ticket']['enc-part']['etype']] temp = krbtgt_cipher.decrypt(krbtgt_key, kc.kerberos_TGT['ticket']['enc-part']['kvno'], krbtgt_data) print(temp.hex()) krbtgt_enc = EncTicketPart.load(temp).native pprint.pprint(krbtgt_enc) #print(krbtgt_enc['authorization-data'][0]['ad-data']) ad_data = AD_IF_RELEVANT.load(krbtgt_enc['authorization-data'][0]['ad-data']) with open('addata.bin','wb') as f: f.write(ad_data.native[0]['ad-data'])
ccred = KerberosCredential() ccred.username = '******' ccred.domain = 'TEST.corp' ccred2 = KerberosCredential() ccred2.username = '******' ccred2.domain = 'TEST.corp' creds = [ccred, ccred2] ks = KerberosSocket('192.168.9.1') ar = APREPRoast(ks) res = ar.run(creds) rep = res[0] print(res) x, a, enctype, checksum, data = rep.split('$') password = '******' cipher = _enctype_table[int(enctype)] key = Key(int(enctype), hashlib.new('md4', password.encode('utf-16-le')).digest()) cipherText = bytes.fromhex(checksum + data) temp = cipher.decrypt(key, 3, cipherText) print() print() print(temp.hex()) enc_as_rep_part = EncASRepPart.load(temp).native #print(enc_as_rep_part)
import logging from minikerberos.common import * from minikerberos.communication import * from minikerberos.ccache import CCACHE from minikerberos.encryption import _enctype_table, Key import pprint ccred_krbtgt = KerberosCredential.from_connection_string( 'DEMO1.FREEIPA.ORG/admin/pass:[email protected]') cipher_data = bytes.fromhex( 'a4134a8692a021f735418c549daad0ae2f7b309c027f20efebf52e0e0b6d0b269e0ff54c2af22cad5c67de121612e2eef0007d70b5798536a94bf9a4ee87da0de9df15ffc65a9e63eabc96edd4fb9659db8a40cb6fe0ee0336d79bc8f1c8df59287d6d8db1180ca00feb581445f57cf9b22c2cbe83354736ecc230948883db7507f094869dfc98bf2b8331690ed3f9e45760d97db175bfba9232366cb3e5faa38aa5770c5e62aa60c3e829e9c2c9835fa255611310a7364f55e90626714b69d9c03cd5c2ee1fb47590dfa403039803b243149bdfe7c3d9d4859969f7e2e3c96ac89f5cb3a4123ffbf2c35b340afd1ad3e1b32b978901b596901c957ce4894f4939b096051e9d3acf4ef942c5bc7806b39d51d0b08bd4fdd52be0f560af2760914ea814fce7b85a0172' ) et = EncryptionType(18) krbtgt_key = Key( 18, ccred_krbtgt.get_key_for_enctype(et, salt="dc{< 5&c0'85-Y4K".encode())) krbtgt_cipher = _enctype_table[18] temp = krbtgt_cipher.decrypt(krbtgt_key, 3, cipher_data) print(temp.hex()) krbtgt_enc = EncTGSRepPart.load(temp).native pprint.pprint(krbtgt_enc) session_key = Key(18, krbtgt_enc['key']['keyvalue']) session_cipher = _enctype_table[18] cipherText = bytes.fromhex( '351505edf3ecbb9fcf59299f28d23fd514e50b884f729ed43e6abf12451448e1e6db7a6da5dec0a39202eaa69b8be5ef4529e2006021fde7a1239d53904c9e06cdab9ba02fcc6b369d2421cdc21e7ee691c2958e3117159c5f572ba86fdd2208207fd15acd036eb11b18bf3654e344b5322463b6bfca45ee3c6e2f57c560fd8d70450a59e6a9b5499b48953017644f99282979c5220a1f6bc76ef9cdc5153ddd133b2541dee35f7c8e4607dd192eabbf' ) temp = session_cipher.decrypt(session_key, 7, cipherText) print(temp.hex()) auth = Authenticator.load(temp).native