Example #1
0
    def decrypt_asrep(self, as_rep):
        def truncate_key(value, keysize):
            output = b''
            currentNum = 0
            while len(output) < keysize:
                currentDigest = hashlib.sha1(bytes([currentNum]) +
                                             value).digest()
                if len(output) + len(currentDigest) > keysize:
                    output += currentDigest[:keysize - len(output)]
                    break
                output += currentDigest
                currentNum += 1

            return output

        for pa in as_rep['padata']:
            if pa['padata-type'] == 17:
                pkasrep = PA_PK_AS_REP.load(pa['padata-value']).native
                break
        else:
            raise Exception('PA_PK_AS_REP not found!')

        sd = cms.SignedData.load(pkasrep['dhSignedData']).native
        keyinfo = sd['encap_content_info']
        if keyinfo['content_type'] != '1.3.6.1.5.2.3.2':
            raise Exception('Keyinfo content type unexpected value')
        authdata = KDCDHKeyInfo.load(keyinfo['content']).native
        pubkey = int(
            ''.join(['1'] + [str(x) for x in authdata['subjectPublicKey']]), 2)

        pubkey = int.from_bytes(core.BitString(
            authdata['subjectPublicKey']).dump()[7:],
                                'big',
                                signed=False)
        shared_key = self.diffie.exchange(pubkey)

        server_nonce = pkasrep['serverDHNonce']
        fullKey = shared_key + self.diffie.dh_nonce + server_nonce

        etype = as_rep['enc-part']['etype']
        cipher = _enctype_table[etype]
        if etype == Enctype.AES256:
            t_key = truncate_key(fullKey, 32)
        elif etype == Enctype.AES128:
            t_key = truncate_key(fullKey, 16)
        elif etype == Enctype.RC4:
            raise NotImplementedError(
                'RC4 key truncation documentation missing. it is different from AES'
            )
            #t_key = truncate_key(fullKey, 16)

        key = Key(cipher.enctype, t_key)
        enc_data = as_rep['enc-part']['cipher']
        dec_data = cipher.decrypt(key, 3, enc_data)
        encasrep = EncASRepPart.load(dec_data).native
        cipher = _enctype_table[int(encasrep['key']['keytype'])]
        session_key = Key(cipher.enctype, encasrep['key']['keyvalue'])
        return encasrep, session_key, cipher
Example #2
0
def decrypt_pk_dh(data, diffieHellmanExchange):
    try:
        rep = SPNEGO_PKINIT_AS_REP.load(bytes.fromhex(data)).native
    except:
        krb_message = KerberosResponse.load(bytes.fromhex(data))
        raise KerberosError(krb_message)

    relevantPadata = None
    for padata in rep['Kerberos']['padata']:
        if padata['padata-type'] == 17:
            relevantPadata = PA_PK_AS_REP.load(padata['padata-value']).native
            break

    if not relevantPadata:
        raise Exception('No PAdata found with type 17')
    keyinfo = SignedData.load(
        relevantPadata['dhSignedData']).native['encap_content_info']
    if keyinfo['content_type'] != '1.3.6.1.5.2.3.2':
        raise Exception('Keyinfo content type unexpected value')
    authdata = KDCDHKeyInfo.load(keyinfo['content']).native
    pubkey = int(
        ''.join(['1'] + [str(x) for x in authdata['subjectPublicKey']]), 2)

    pubkey = int.from_bytes(core.BitString(
        authdata['subjectPublicKey']).dump()[7:],
                            'big',
                            signed=False)
    shared_key = diffieHellmanExchange.exchange(pubkey)

    server_nonce = relevantPadata['serverDHNonce']
    fullKey = shared_key + diffieHellmanExchange.dh_nonce + server_nonce

    etype = rep['Kerberos']['enc-part']['etype']
    cipher = _enctype_table[etype]
    if etype == Enctype.AES256:
        t_key = truncate(fullKey, 32)
    elif etype == Enctype.AES128:
        t_key = truncate(fullKey, 16)
    elif etype == Enctype.RC4:
        raise NotImplementedError(
            'RC4 key truncation documentation missing. it is different from AES'
        )

    key = Key(cipher.enctype, t_key)
    enc_data = rep['Kerberos']['enc-part']['cipher']
    dec_data = cipher.decrypt(key, 3, enc_data)
    encasrep = EncASRepPart.load(dec_data).native
    cipher = _enctype_table[int(encasrep['key']['keytype'])]
    session_key = Key(cipher.enctype, encasrep['key']['keyvalue'])

    return session_key, cipher, rep

    # remove Octet String manualy
    padata = str(rep['padata'][0]['padata-value']).encode('hex')
    parsedPadata = decode(padata.decode('hex'), asn1Spec=AS_REP_Padata())[0]
    decoded = parsedPadata['DHRepInfo']['dhSignedData']
    kdcSignedDataResponse = decode(decoded, asn1Spec=SignedData())[0]
    kdcDHKeyInfo = str(kdcSignedDataResponse['encapContentInfo']
                       ['id-pkinit-authData-value']).encode('hex')
    d = decode(kdcDHKeyInfo.decode('hex'), asn1Spec=KDCDHKeyInfo())[0]
    dcPublicKey = int(encode(d['subjectPublicKey']).encode('hex')[20:], 16)

    dcPublicNumbers = dh.DHPublicNumbers(dcPublicKey, diffieHellmanExchange[2])

    backend = default_backend()

    dcPublicKey = backend.load_dh_public_numbers(dcPublicNumbers)
    shared_key = diffieHellmanExchange[1].exchange(dcPublicKey)
    sharedHexKey = shared_key.encode('hex')

    clientDHNonce = '6B328FA66EEBDFD3D69ED34E5007776AB30832A2ED1DCB1699781BFE0BEDF87A'
    serverDHNonce = encode(
        parsedPadata['DHRepInfo']['encKeyPack']).encode('hex')[8:]

    fullKey = sharedHexKey + clientDHNonce + serverDHNonce

    etype = rep['enc-part']['etype']
    cipher = _enctype_table[etype]
    if etype == Enctype.AES256:
        truncateKey = truncate(fullKey, 32)
        key = Key(cipher.enctype, truncateKey)

    elif etype == Enctype.AES128:
        truncateKey = truncate(fullKey, 16)
        key = Key(cipher.enctype, truncateKey)

    elif etype == Enctype.RC4:
        truncateKey = truncate(fullKey, 16)
        key = Key(cipher.enctype, truncateKey)

    cipherText = rep['enc-part']['cipher'].asOctets()
    plainText = cipher.decrypt(key, 3, cipherText)
    encASRepPart = decode(plainText, asn1Spec=EncASRepPart())[0]
    cipher = _enctype_table[int(encASRepPart['key']['keytype'])]
    session_key = Key(cipher.enctype,
                      encASRepPart['key']['keyvalue'].asOctets())
    return session_key, cipher, rep
Example #3
0
	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