def build_as_req_negoEx(user_cert, cert_pass, remoteComputer, diffieHellmanExchange): pfx = open(user_cert, 'rb').read() privkeyinfo, certificate, extra_certs = parse_pkcs12( pfx, password=cert_pass.encode()) privkey = load_private_key(privkeyinfo) issuer = certificate.issuer.native['common_name'] cname = "AzureAD\\" + issuer + "\\" + [ i for i in certificate.subject.native['common_name'] if i.startswith('S-1') ][0] now = datetime.datetime.now(datetime.timezone.utc) req_body = build_req_body_NegoEx(remoteComputer, cname, now) padata = BuildPkinit_pa(req_body, now, diffieHellmanExchange, privkey, certificate) payload = PA_PK_AS_REQ() payload['signedAuthPack'] = padata pa_data = { 'padata-type': PaDataType.PK_AS_REQ.value, 'padata-value': payload.dump() } asreq = { 'pvno': 5, 'msg-type': 10, 'padata': [pa_data], 'req-body': req_body } req = { 'kerberos-v5': algos.DigestAlgorithmId('1.3.6.1.5.2.7'), 'null': core.Null(), 'Kerberos': AS_REQ(asreq) } req = SPNEGO_PKINIT_REP(req) return issuer, req.dump().hex()
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)
def construct_tgt_req(self): now = now = datetime.datetime.now(datetime.timezone.utc) 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.spn.username] }) kdc_req_body['realm'] = self.spn.domain.upper() kdc_req_body['sname'] = PrincipalName({ 'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': ['krbtgt', self.spn.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'] = [2, 3, 16, 23, 17, 18] #we "support" all MS related enctypes 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) return AS_REQ(kdc_req)
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 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 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('[getTGT] Generating initial TGT without authentication data') now = datetime.datetime.now(datetime.timezone.utc) 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) 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('[getTGT] 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 etype = self.kerberos_TGT['enc-part']['etype'] #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 = _enctype_table[etype] self.kerberos_cipher_type = etype encryption_type = EncryptionType(self.kerberos_cipher.enctype) enctype = self.usercreds.get_key_for_enctype(encryption_type) self.kerberos_key = Key(self.kerberos_cipher.enctype, enctype) else: if rep.native['error-code'] != KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED.value: raise KerberosError(rep) rep = rep.native logger.debug('[getTGT] Got reply from server, asking to provide auth data') rep = self.do_preauth(rep) logger.debug('[getTGT] Got valid response from server') rep = rep.native self.kerberos_TGT = rep cipherText = self.kerberos_TGT['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('[getTGT] EncAsRepPart load failed, is this linux?') try: self.kerberos_TGT_encpart = EncTGSRepPart.load(temp).native except Exception as e: logger.error('[getTGT] Failed to load decrypted part of the reply!') raise e 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('[getTGT] Got valid TGT') return
def build_asreq( self, target=None, cname=None, kdcopts=['forwardable', 'renewable', 'proxiable', 'canonicalize']): if isinstance(kdcopts, list): kdcopts = set(kdcopts) if cname is not None: if isinstance(cname, str): cname = [cname] else: cname = [self.cname] if target is not None: if isinstance(target, str): target = [target] else: target = ['127.0.0.1'] now = datetime.datetime.now(datetime.timezone.utc) kdc_req_body_data = {} kdc_req_body_data['kdc-options'] = KDCOptions(kdcopts) kdc_req_body_data['cname'] = PrincipalName({ 'name-type': NAME_TYPE.MS_PRINCIPAL.value, 'name-string': cname }) kdc_req_body_data['realm'] = 'WELLKNOWN:PKU2U' kdc_req_body_data['sname'] = PrincipalName({ 'name-type': NAME_TYPE.MS_PRINCIPAL.value, 'name-string': target }) kdc_req_body_data['till'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) kdc_req_body_data['rtime'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) kdc_req_body_data['nonce'] = secrets.randbits(31) kdc_req_body_data['etype'] = [18, 17] # 23 breaks... kdc_req_body_data['addresses'] = [ HostAddress({ 'addr-type': 20, 'address': b'127.0.0.1' }) ] # not sure if this is needed kdc_req_body = KDC_REQ_BODY(kdc_req_body_data) checksum = hashlib.sha1(kdc_req_body.dump()).digest() authenticator = {} authenticator['cusec'] = now.microsecond authenticator['ctime'] = now.replace(microsecond=0) authenticator['nonce'] = secrets.randbits(31) authenticator['paChecksum'] = checksum dp = {} dp['p'] = self.diffie.p dp['g'] = self.diffie.g dp['q'] = 0 # mandatory parameter, but it is not needed pka = {} pka['algorithm'] = '1.2.840.10046.2.1' pka['parameters'] = keys.DomainParameters(dp) spki = {} spki['algorithm'] = keys.PublicKeyAlgorithm(pka) spki['public_key'] = self.diffie.get_public_key() authpack = {} authpack['pkAuthenticator'] = PKAuthenticator(authenticator) authpack['clientPublicValue'] = keys.PublicKeyInfo(spki) authpack['clientDHNonce'] = self.diffie.dh_nonce authpack = AuthPack(authpack) signed_authpack = self.sign_authpack(authpack.dump(), wrap_signed=False) # ??????? This is absolutely nonsense, payload = length_encode(len(signed_authpack)) + signed_authpack payload = b'\x80' + payload signed_authpack = b'\x30' + length_encode(len(payload)) + payload pa_data_1 = {} pa_data_1['padata-type'] = PaDataType.PK_AS_REQ.value pa_data_1['padata-value'] = signed_authpack asreq = {} asreq['pvno'] = 5 asreq['msg-type'] = 10 asreq['padata'] = [pa_data_1] asreq['req-body'] = kdc_req_body return AS_REQ(asreq).dump()
def build_asreq_pkinit( self, supported_encryption_method, kdcopts=['forwardable', 'renewable', 'renewable-ok']): from asn1crypto import keys if supported_encryption_method.value == 23: raise Exception( 'RC4 encryption is not supported for certificate auth!') now = datetime.datetime.now(datetime.timezone.utc) kdc_req_body_data = {} kdc_req_body_data['kdc-options'] = KDCOptions(set(kdcopts)) kdc_req_body_data['cname'] = PrincipalName({ 'name-type': NAME_TYPE.PRINCIPAL.value, 'name-string': [self.usercreds.username] }) kdc_req_body_data['realm'] = self.usercreds.domain.upper() kdc_req_body_data['sname'] = PrincipalName({ 'name-type': NAME_TYPE.SRV_INST.value, 'name-string': ['krbtgt', self.usercreds.domain.upper()] }) kdc_req_body_data['till'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) kdc_req_body_data['rtime'] = (now + datetime.timedelta(days=1)).replace( microsecond=0) kdc_req_body_data['nonce'] = secrets.randbits(31) kdc_req_body_data['etype'] = [supported_encryption_method.value ] #[18,17] # 23 breaks... kdc_req_body = KDC_REQ_BODY(kdc_req_body_data) checksum = hashlib.sha1(kdc_req_body.dump()).digest() authenticator = {} authenticator['cusec'] = now.microsecond authenticator['ctime'] = now.replace(microsecond=0) authenticator['nonce'] = secrets.randbits(31) authenticator['paChecksum'] = checksum dp = {} dp['p'] = self.usercreds.dhparams.p dp['g'] = self.usercreds.dhparams.g dp['q'] = 0 # mandatory parameter, but it is not needed pka = {} pka['algorithm'] = '1.2.840.10046.2.1' pka['parameters'] = keys.DomainParameters(dp) spki = {} spki['algorithm'] = keys.PublicKeyAlgorithm(pka) spki['public_key'] = self.usercreds.dhparams.get_public_key() authpack = {} authpack['pkAuthenticator'] = PKAuthenticator(authenticator) authpack['clientPublicValue'] = keys.PublicKeyInfo(spki) authpack['clientDHNonce'] = self.usercreds.dhparams.dh_nonce authpack = AuthPack(authpack) signed_authpack = self.usercreds.sign_authpack(authpack.dump(), wrap_signed=True) payload = PA_PK_AS_REQ() payload['signedAuthPack'] = signed_authpack pa_data_1 = {} pa_data_1['padata-type'] = PaDataType.PK_AS_REQ.value pa_data_1['padata-value'] = payload.dump() pa_data_0 = {} pa_data_0['padata-type'] = int(PADATA_TYPE('PA-PAC-REQUEST')) pa_data_0['padata-value'] = PA_PAC_REQUEST({ 'include-pac': True }).dump() asreq = {} asreq['pvno'] = 5 asreq['msg-type'] = 10 asreq['padata'] = [pa_data_0, pa_data_1] asreq['req-body'] = kdc_req_body return AS_REQ(asreq)