def get_something(self, tgs, encTGSRepPart, sessionkey): now = datetime.datetime.utcnow() 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 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_req['authenticator'] = EncryptedData({ 'etype': self.kerberos_cipher_type, 'cipher': authenticator_data_enc }) return AP_REQ(ap_req).dump()
def S4U2proxy(self, s4uself_ticket, spn_user, supp_enc_methods=SUPPORTED_METHODS): now = datetime.datetime.utcnow() 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( 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_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) 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_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) try: return 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
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())
def to_string(self): return as_str(self.data)